more structure

This commit is contained in:
Radovan Bast 2016-04-19 22:52:39 +02:00
parent 1a2bd4507b
commit f2ef8bf393
5 changed files with 229 additions and 237 deletions

28
autocmake/http.py Normal file
View 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
View 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
View 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
View 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
View File

@ -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)