more structure
This commit is contained in:
parent
1a2bd4507b
commit
f2ef8bf393
28
autocmake/http.py
Normal file
28
autocmake/http.py
Normal file
@ -0,0 +1,28 @@
|
||||
def fetch_url(src, dst):
|
||||
"""
|
||||
Fetch file from URL src and save it to dst.
|
||||
"""
|
||||
# we do not use the nicer sys.version_info.major
|
||||
# for compatibility with Python < 2.7
|
||||
if sys.version_info[0] > 2:
|
||||
import urllib.request
|
||||
|
||||
class URLopener(urllib.request.FancyURLopener):
|
||||
def http_error_default(self, url, fp, errcode, errmsg, headers):
|
||||
sys.stderr.write("ERROR: could not fetch {0}\n".format(url))
|
||||
sys.exit(-1)
|
||||
else:
|
||||
import urllib
|
||||
|
||||
class URLopener(urllib.FancyURLopener):
|
||||
def http_error_default(self, url, fp, errcode, errmsg, headers):
|
||||
sys.stderr.write("ERROR: could not fetch {0}\n".format(url))
|
||||
sys.exit(-1)
|
||||
|
||||
dirname = os.path.dirname(dst)
|
||||
if dirname != '':
|
||||
if not os.path.isdir(dirname):
|
||||
os.makedirs(dirname)
|
||||
|
||||
opener = URLopener()
|
||||
opener.retrieve(src, dst)
|
47
autocmake/interpolate.py
Normal file
47
autocmake/interpolate.py
Normal file
@ -0,0 +1,47 @@
|
||||
def replace(s, d):
|
||||
from re import findall
|
||||
if isinstance(s, str):
|
||||
for var in findall(r"%\(([A-Za-z0-9_]*)\)", s):
|
||||
s = s.replace("%({})".format(var), str(d[var]))
|
||||
return s
|
||||
|
||||
|
||||
def test_replace():
|
||||
assert replace('hey %(foo) ho %(bar)',
|
||||
{'foo': 'hey', 'bar': 'ho'}) == 'hey hey ho ho'
|
||||
|
||||
|
||||
def interpolate(d, d_map):
|
||||
from collections import Mapping, Iterable
|
||||
from copy import copy
|
||||
for k, v in d.items():
|
||||
if isinstance(v, Mapping):
|
||||
d[k] = interpolate(d[k], d_map)
|
||||
elif isinstance(v, Iterable) and not isinstance(v, str):
|
||||
l = []
|
||||
for x in v:
|
||||
if isinstance(x, Mapping):
|
||||
l.append(interpolate(x, d_map))
|
||||
else:
|
||||
l.append(replace(x, d_map))
|
||||
d[k] = copy(l)
|
||||
else:
|
||||
d[k] = replace(d[k], d_map)
|
||||
return d
|
||||
|
||||
|
||||
def test_interpolate():
|
||||
d = {'foo': 'hey',
|
||||
'bar': 'ho',
|
||||
'one': 'hey %(foo) ho %(bar)',
|
||||
'two': {'one': 'hey %(foo) ho %(bar)',
|
||||
'two': 'raboof'}}
|
||||
d_interpolated = {'foo': 'hey',
|
||||
'bar': 'ho',
|
||||
'one': 'hey hey ho ho',
|
||||
'two': {'one': 'hey hey ho ho',
|
||||
'two': 'raboof'}}
|
||||
assert interpolate(d, d) == d_interpolated
|
||||
d2 = {'modules': [{'fc': [{'source': '%(url_root)fc_optional.cmake'}]}], 'url_root': 'downloaded/downloaded_'}
|
||||
d2_interpolated = {'modules': [{'fc': [{'source': 'downloaded/downloaded_fc_optional.cmake'}]}], 'url_root': 'downloaded/downloaded_'}
|
||||
assert interpolate(d2, d2) == d2_interpolated
|
133
autocmake/parse_rst.py
Normal file
133
autocmake/parse_rst.py
Normal file
@ -0,0 +1,133 @@
|
||||
def parse_cmake_module(s_in, override={}):
|
||||
import sys
|
||||
from collections import Mapping, Iterable, defaultdict
|
||||
from autocmake.parse_yaml import parse_yaml
|
||||
|
||||
# we do not use the nicer sys.version_info.major
|
||||
# for compatibility with Python < 2.7
|
||||
if sys.version_info[0] > 2:
|
||||
from io import StringIO
|
||||
else:
|
||||
from StringIO import StringIO
|
||||
|
||||
parsed_config = defaultdict(lambda: None)
|
||||
|
||||
if 'autocmake.yml configuration::' not in s_in:
|
||||
return parsed_config
|
||||
|
||||
s_out = []
|
||||
is_rst_line = False
|
||||
for line in s_in.split('\n'):
|
||||
if is_rst_line:
|
||||
if len(line) > 0:
|
||||
if line[0] != '#':
|
||||
is_rst_line = False
|
||||
else:
|
||||
is_rst_line = False
|
||||
if is_rst_line:
|
||||
s_out.append(line[2:])
|
||||
if '#.rst:' in line:
|
||||
is_rst_line = True
|
||||
|
||||
autocmake_entry = '\n'.join(s_out).split('autocmake.yml configuration::')[1]
|
||||
autocmake_entry = autocmake_entry.replace('\n ', '\n')
|
||||
|
||||
buf = StringIO(autocmake_entry)
|
||||
config = parse_yaml(buf, override)
|
||||
|
||||
for k, v in config.items():
|
||||
if isinstance(v, Iterable) and not isinstance(v, str):
|
||||
parsed_config[k] = [x for x in config[k]]
|
||||
else:
|
||||
parsed_config[k] = [config[k]]
|
||||
|
||||
return parsed_config
|
||||
|
||||
|
||||
def test_parse_cmake_module():
|
||||
|
||||
s = r'''#.rst:
|
||||
#
|
||||
# Foo ...
|
||||
#
|
||||
# autocmake.yml configuration::
|
||||
#
|
||||
# docopt:
|
||||
# - "--cxx=<CXX> C++ compiler [default: g++]."
|
||||
# - "--extra-cxx-flags=<EXTRA_CXXFLAGS> Extra C++ compiler flags [default: '']."
|
||||
# export: "'CXX={0}'.format(arguments['--cxx'])"
|
||||
# define: "'-DEXTRA_CXXFLAGS=\"{0}\"'.format(arguments['--extra-cxx-flags'])"
|
||||
|
||||
enable_language(CXX)
|
||||
|
||||
if(NOT DEFINED CMAKE_C_COMPILER_ID)
|
||||
message(FATAL_ERROR "CMAKE_C_COMPILER_ID variable is not defined!")
|
||||
endif()'''
|
||||
|
||||
parsed_config = parse_cmake_module(s)
|
||||
assert parsed_config['docopt'] == ["--cxx=<CXX> C++ compiler [default: g++].", "--extra-cxx-flags=<EXTRA_CXXFLAGS> Extra C++ compiler flags [default: '']."]
|
||||
|
||||
|
||||
def test_parse_cmake_module_no_key():
|
||||
|
||||
s = '''#.rst:
|
||||
#
|
||||
# Foo ...
|
||||
#
|
||||
# Bar ...
|
||||
|
||||
enable_language(CXX)
|
||||
|
||||
if(NOT DEFINED CMAKE_C_COMPILER_ID)
|
||||
message(FATAL_ERROR "CMAKE_C_COMPILER_ID variable is not defined!")
|
||||
endif()'''
|
||||
|
||||
parsed_config = parse_cmake_module(s)
|
||||
assert parsed_config['docopt'] is None
|
||||
|
||||
|
||||
def test_parse_cmake_module_interpolate():
|
||||
|
||||
s = r'''#.rst:
|
||||
#
|
||||
# Foo ...
|
||||
#
|
||||
# autocmake.yml configuration::
|
||||
#
|
||||
# major: 1
|
||||
# minor: 2
|
||||
# patch: 3
|
||||
# a: v%(major)
|
||||
# b: v%(minor)
|
||||
# c: v%(patch)
|
||||
|
||||
enable_language(CXX)'''
|
||||
|
||||
parsed_config = parse_cmake_module(s)
|
||||
assert parsed_config['a'] == ['v1']
|
||||
assert parsed_config['b'] == ['v2']
|
||||
assert parsed_config['c'] == ['v3']
|
||||
|
||||
|
||||
def test_parse_cmake_module_override():
|
||||
|
||||
s = r'''#.rst:
|
||||
#
|
||||
# Foo ...
|
||||
#
|
||||
# autocmake.yml configuration::
|
||||
#
|
||||
# major: 1
|
||||
# minor: 2
|
||||
# patch: 3
|
||||
# a: v%(major)
|
||||
# b: v%(minor)
|
||||
# c: v%(patch)
|
||||
|
||||
enable_language(CXX)'''
|
||||
|
||||
d = {'minor': 4}
|
||||
parsed_config = parse_cmake_module(s, d)
|
||||
assert parsed_config['a'] == ['v1']
|
||||
assert parsed_config['b'] == ['v4']
|
||||
assert parsed_config['c'] == ['v3']
|
16
autocmake/parse_yaml.py
Normal file
16
autocmake/parse_yaml.py
Normal file
@ -0,0 +1,16 @@
|
||||
def parse_yaml(stream, override={}):
|
||||
import yaml
|
||||
from autocmake.interpolate import interpolate
|
||||
|
||||
try:
|
||||
config = yaml.load(stream, yaml.SafeLoader)
|
||||
except yaml.YAMLError as exc:
|
||||
print(exc)
|
||||
sys.exit(-1)
|
||||
|
||||
for k in config:
|
||||
if k in override:
|
||||
config[k] = override[k]
|
||||
|
||||
config = interpolate(config, config)
|
||||
return config
|
242
update.py
242
update.py
@ -8,119 +8,12 @@ import collections
|
||||
|
||||
__version__ = 'X.Y.Z'
|
||||
|
||||
# we do not use the nicer sys.version_info.major
|
||||
# for compatibility with Python < 2.7
|
||||
if sys.version_info[0] > 2:
|
||||
from io import StringIO
|
||||
import urllib.request
|
||||
|
||||
class URLopener(urllib.request.FancyURLopener):
|
||||
def http_error_default(self, url, fp, errcode, errmsg, headers):
|
||||
sys.stderr.write("ERROR: could not fetch {0}\n".format(url))
|
||||
sys.exit(-1)
|
||||
else:
|
||||
from StringIO import StringIO
|
||||
import urllib
|
||||
|
||||
class URLopener(urllib.FancyURLopener):
|
||||
def http_error_default(self, url, fp, errcode, errmsg, headers):
|
||||
sys.stderr.write("ERROR: could not fetch {0}\n".format(url))
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
AUTOCMAKE_GITHUB_URL = 'https://github.com/coderefinery/autocmake/raw/yaml/'
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def replace(s, d):
|
||||
from re import findall
|
||||
if isinstance(s, str):
|
||||
for var in findall(r"%\(([A-Za-z0-9_]*)\)", s):
|
||||
s = s.replace("%({})".format(var), str(d[var]))
|
||||
return s
|
||||
|
||||
|
||||
def test_replace():
|
||||
assert replace('hey %(foo) ho %(bar)',
|
||||
{'foo': 'hey', 'bar': 'ho'}) == 'hey hey ho ho'
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def interpolate(d, d_map):
|
||||
from collections import Mapping, Iterable
|
||||
from copy import copy
|
||||
for k, v in d.items():
|
||||
if isinstance(v, Mapping):
|
||||
d[k] = interpolate(d[k], d_map)
|
||||
elif isinstance(v, Iterable) and not isinstance(v, str):
|
||||
l = []
|
||||
for x in v:
|
||||
if isinstance(x, Mapping):
|
||||
l.append(interpolate(x, d_map))
|
||||
else:
|
||||
l.append(replace(x, d_map))
|
||||
d[k] = copy(l)
|
||||
else:
|
||||
d[k] = replace(d[k], d_map)
|
||||
return d
|
||||
|
||||
|
||||
def test_interpolate():
|
||||
d = {'foo': 'hey',
|
||||
'bar': 'ho',
|
||||
'one': 'hey %(foo) ho %(bar)',
|
||||
'two': {'one': 'hey %(foo) ho %(bar)',
|
||||
'two': 'raboof'}}
|
||||
d_interpolated = {'foo': 'hey',
|
||||
'bar': 'ho',
|
||||
'one': 'hey hey ho ho',
|
||||
'two': {'one': 'hey hey ho ho',
|
||||
'two': 'raboof'}}
|
||||
assert interpolate(d, d) == d_interpolated
|
||||
d2 = {'modules': [{'fc': [{'source': '%(url_root)fc_optional.cmake'}]}], 'url_root': 'downloaded/downloaded_'}
|
||||
d2_interpolated = {'modules': [{'fc': [{'source': 'downloaded/downloaded_fc_optional.cmake'}]}], 'url_root': 'downloaded/downloaded_'}
|
||||
assert interpolate(d2, d2) == d2_interpolated
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def fetch_url(src, dst):
|
||||
"""
|
||||
Fetch file from URL src and save it to dst.
|
||||
"""
|
||||
dirname = os.path.dirname(dst)
|
||||
if dirname != '':
|
||||
if not os.path.isdir(dirname):
|
||||
os.makedirs(dirname)
|
||||
|
||||
opener = URLopener()
|
||||
opener.retrieve(src, dst)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def parse_yaml(stream, override={}):
|
||||
import yaml
|
||||
|
||||
try:
|
||||
config = yaml.load(stream, yaml.SafeLoader)
|
||||
except yaml.YAMLError as exc:
|
||||
print(exc)
|
||||
sys.exit(-1)
|
||||
|
||||
for k in config:
|
||||
if k in override:
|
||||
config[k] = override[k]
|
||||
|
||||
config = interpolate(config, config)
|
||||
return config
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def print_progress_bar(text, done, total, width):
|
||||
"""
|
||||
Print progress bar.
|
||||
@ -349,6 +242,7 @@ def fetch_modules(config, relative_path):
|
||||
be included in CMakeLists.txt.
|
||||
"""
|
||||
from collections import Iterable
|
||||
from autocmake.http import fetch_url
|
||||
|
||||
download_directory = 'downloaded'
|
||||
if not os.path.exists(download_directory):
|
||||
@ -431,6 +325,9 @@ def main(argv):
|
||||
"""
|
||||
Main function.
|
||||
"""
|
||||
from autocmake.parse_yaml import parse_yaml
|
||||
from autocmake.http import fetch_url
|
||||
|
||||
if len(argv) != 2:
|
||||
sys.stderr.write("\nYou can update a project in two steps.\n\n")
|
||||
sys.stderr.write("Step 1: Update or create infrastructure files\n")
|
||||
@ -464,6 +361,7 @@ def main(argv):
|
||||
for f in ['autocmake/configure.py',
|
||||
'autocmake/external/docopt.py',
|
||||
'autocmake/__init__.py',
|
||||
'autocmake/interpolate.py',
|
||||
'update.py']:
|
||||
print('- fetching {0}'.format(f))
|
||||
fetch_url(
|
||||
@ -535,135 +433,5 @@ def make_executable(path):
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def parse_cmake_module(s_in, override={}):
|
||||
from collections import Mapping, Iterable
|
||||
|
||||
parsed_config = collections.defaultdict(lambda: None)
|
||||
|
||||
if 'autocmake.yml configuration::' not in s_in:
|
||||
return parsed_config
|
||||
|
||||
s_out = []
|
||||
is_rst_line = False
|
||||
for line in s_in.split('\n'):
|
||||
if is_rst_line:
|
||||
if len(line) > 0:
|
||||
if line[0] != '#':
|
||||
is_rst_line = False
|
||||
else:
|
||||
is_rst_line = False
|
||||
if is_rst_line:
|
||||
s_out.append(line[2:])
|
||||
if '#.rst:' in line:
|
||||
is_rst_line = True
|
||||
|
||||
autocmake_entry = '\n'.join(s_out).split('autocmake.yml configuration::')[1]
|
||||
autocmake_entry = autocmake_entry.replace('\n ', '\n')
|
||||
|
||||
buf = StringIO(autocmake_entry)
|
||||
config = parse_yaml(buf, override)
|
||||
|
||||
for k, v in config.items():
|
||||
if isinstance(v, Iterable) and not isinstance(v, str):
|
||||
parsed_config[k] = [x for x in config[k]]
|
||||
else:
|
||||
parsed_config[k] = [config[k]]
|
||||
|
||||
return parsed_config
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_parse_cmake_module():
|
||||
|
||||
s = r'''#.rst:
|
||||
#
|
||||
# Foo ...
|
||||
#
|
||||
# autocmake.yml configuration::
|
||||
#
|
||||
# docopt:
|
||||
# - "--cxx=<CXX> C++ compiler [default: g++]."
|
||||
# - "--extra-cxx-flags=<EXTRA_CXXFLAGS> Extra C++ compiler flags [default: '']."
|
||||
# export: "'CXX={0}'.format(arguments['--cxx'])"
|
||||
# define: "'-DEXTRA_CXXFLAGS=\"{0}\"'.format(arguments['--extra-cxx-flags'])"
|
||||
|
||||
enable_language(CXX)
|
||||
|
||||
if(NOT DEFINED CMAKE_C_COMPILER_ID)
|
||||
message(FATAL_ERROR "CMAKE_C_COMPILER_ID variable is not defined!")
|
||||
endif()'''
|
||||
|
||||
parsed_config = parse_cmake_module(s)
|
||||
assert parsed_config['docopt'] == ["--cxx=<CXX> C++ compiler [default: g++].", "--extra-cxx-flags=<EXTRA_CXXFLAGS> Extra C++ compiler flags [default: '']."]
|
||||
|
||||
|
||||
def test_parse_cmake_module_no_key():
|
||||
|
||||
s = '''#.rst:
|
||||
#
|
||||
# Foo ...
|
||||
#
|
||||
# Bar ...
|
||||
|
||||
enable_language(CXX)
|
||||
|
||||
if(NOT DEFINED CMAKE_C_COMPILER_ID)
|
||||
message(FATAL_ERROR "CMAKE_C_COMPILER_ID variable is not defined!")
|
||||
endif()'''
|
||||
|
||||
parsed_config = parse_cmake_module(s)
|
||||
assert parsed_config['docopt'] is None
|
||||
|
||||
|
||||
def test_parse_cmake_module_interpolate():
|
||||
|
||||
s = r'''#.rst:
|
||||
#
|
||||
# Foo ...
|
||||
#
|
||||
# autocmake.yml configuration::
|
||||
#
|
||||
# major: 1
|
||||
# minor: 2
|
||||
# patch: 3
|
||||
# a: v%(major)
|
||||
# b: v%(minor)
|
||||
# c: v%(patch)
|
||||
|
||||
enable_language(CXX)'''
|
||||
|
||||
parsed_config = parse_cmake_module(s)
|
||||
assert parsed_config['a'] == ['v1']
|
||||
assert parsed_config['b'] == ['v2']
|
||||
assert parsed_config['c'] == ['v3']
|
||||
|
||||
|
||||
def test_parse_cmake_module_override():
|
||||
|
||||
s = r'''#.rst:
|
||||
#
|
||||
# Foo ...
|
||||
#
|
||||
# autocmake.yml configuration::
|
||||
#
|
||||
# major: 1
|
||||
# minor: 2
|
||||
# patch: 3
|
||||
# a: v%(major)
|
||||
# b: v%(minor)
|
||||
# c: v%(patch)
|
||||
|
||||
enable_language(CXX)'''
|
||||
|
||||
d = {'minor': 4}
|
||||
parsed_config = parse_cmake_module(s, d)
|
||||
assert parsed_config['a'] == ['v1']
|
||||
assert parsed_config['b'] == ['v4']
|
||||
assert parsed_config['c'] == ['v3']
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
|
Loading…
x
Reference in New Issue
Block a user