├── .gitignore ├── LICENSE ├── MANIFEST.in ├── bin └── confu ├── confu ├── __init__.py ├── __main__.py ├── arm │ ├── __init__.py │ └── isa.py ├── builds │ ├── __init__.py │ ├── base.py │ ├── deps.py │ ├── emscripten.py │ ├── module.py │ ├── pnacl.py │ ├── state.py │ └── unix.py ├── git.py ├── globals.py ├── isa.py ├── manifest.py ├── platform.py ├── recipes │ ├── __init__.py │ ├── googlebenchmark.py │ ├── googlebenchmark.yaml │ ├── googletest.py │ └── googletest.yaml ├── results │ ├── __init__.py │ ├── build.py │ ├── collection.py │ └── compilcation.py ├── tools │ ├── __init__.py │ ├── base.py │ ├── collection.py │ ├── peachpy.py │ └── toolchains │ │ ├── __init__.py │ │ ├── android.py │ │ ├── base.py │ │ ├── emscripten.py │ │ ├── nacl.py │ │ └── unix.py ├── utils.py ├── validators.py └── x86 │ ├── __init__.py │ └── isa.py ├── install_pygit2.sh ├── requirements.txt ├── setup.py └── sphinx ├── conf.py ├── confu.rst ├── index.rst └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Ninja files 2 | .ninja_deps 3 | .ninja_log 4 | build.ninja 5 | 6 | # PyCharm project files 7 | .idea/ 8 | 9 | # Build objects and artifacts 10 | confu.egg-info/ 11 | dist/ 12 | docs/ 13 | build/ 14 | *.pyc 15 | *.pyo 16 | 17 | # System files 18 | .DS_Store 19 | .DS_Store? 20 | ._* 21 | .Spotlight-V100 22 | .Trashes 23 | ehthumbs.db 24 | Thumbs.db 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Facebook Inc. 4 | Copyright (c) 2017 Georgia Institute of Technology 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include confu/recipes/*.yaml 2 | -------------------------------------------------------------------------------- /bin/confu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from confu.__main__ import main 4 | 5 | if __name__ == '__main__': 6 | main() 7 | -------------------------------------------------------------------------------- /confu/__init__.py: -------------------------------------------------------------------------------- 1 | __copyright__ = "Copyright 2017, Georgia Institute of Technology" 2 | __license__ = "MIT" 3 | __version_info__ = ('0', '0', '1') 4 | __version__ = '.'.join(__version_info__) 5 | __maintainer__ = "Marat Dukhan" 6 | __email__ = "maratek@gmail.com" 7 | 8 | import logging 9 | logger = logging.getLogger("confu") 10 | logger.setLevel(logging.INFO) 11 | 12 | console_handler = logging.StreamHandler() 13 | console_handler.setLevel(logging.INFO) 14 | 15 | 16 | class ConsoleFormatter(logging.Formatter): 17 | def __init__(self): 18 | super(ConsoleFormatter, self).__init__("%(message)s") 19 | 20 | def format(self, record): 21 | message = super(ConsoleFormatter, self).format(record) 22 | if record.levelname in ["DEBUG", "INFO"]: 23 | return message[0].upper() + message[1:] 24 | else: 25 | return { 26 | "WARNING": "Warning", "ERROR": "Error", "CRITICAL": "Fatal error" 27 | }[record.levelname] + ": " + message[0].lower() + message[1:] 28 | 29 | console_formatter = ConsoleFormatter() 30 | console_handler.setFormatter(console_formatter) 31 | logger.addHandler(console_handler) 32 | 33 | 34 | from confu.builds import Build 35 | from confu.platform import Platform 36 | 37 | 38 | def standard_parser(description="Confu configuration script"): 39 | import argparse 40 | 41 | from os import linesep 42 | from confu.platform import host, possible_targets 43 | 44 | parser = argparse.ArgumentParser(description=description, 45 | formatter_class=argparse.RawTextHelpFormatter) 46 | parser.add_argument("--target", dest="target", metavar="PLATFORM", type=Platform, 47 | default=host.name, 48 | help="platform where the code will run. Potential options:" + linesep + 49 | " " + host.name + " (default)" + linesep + 50 | linesep.join(" " + target for target in possible_targets[1:])) 51 | parser.add_argument("--toolchain", dest="toolchain", metavar="TOOLCHAIN", 52 | choices=["auto", "gnu", "clang"], default="auto", 53 | help="toolchain to use for compilation. Potential options:" + linesep + 54 | linesep.join(" " + name for name in ["auto (default)", "gnu", "clang"])) 55 | 56 | 57 | 58 | return parser 59 | -------------------------------------------------------------------------------- /confu/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import argparse 4 | import logging 5 | 6 | 7 | logger = logging.getLogger("confu") 8 | 9 | 10 | def setup_deps(options, unparsed_args): 11 | import confu.recipes 12 | import types 13 | builtin_recipes = [name for name in confu.recipes.__dict__ 14 | if isinstance(confu.recipes.__dict__[name], types.ModuleType)] 15 | 16 | preparsed_args = list() 17 | for arg in unparsed_args: 18 | # TODO: more robust checks 19 | if arg.startswith("--with-") and "=" in arg: 20 | preparsed_args += arg.split("=", 1) 21 | else: 22 | preparsed_args.append(arg) 23 | 24 | dependency_paths = dict() 25 | 26 | import os 27 | for with_arg, path in zip(preparsed_args[0::2], preparsed_args[1::2]): 28 | path = os.path.abspath(os.path.expanduser(os.path.normpath(path))) 29 | dep_name = with_arg[len("--with-"):] 30 | dependency_paths[dep_name] = path 31 | 32 | def setup_project_deps(project_dir, root_dir, namespace=""): 33 | import confu.manifest 34 | project = confu.manifest.Project.from_root(project_dir) 35 | 36 | deps_dir = os.path.join(root_dir, "deps") 37 | if project.deps and not os.path.isdir(deps_dir): 38 | os.mkdir(deps_dir) 39 | 40 | for dep in project.deps: 41 | dep_dir = os.path.join(deps_dir, dep.name) 42 | qualified_name = dep.name if not namespace else namespace + ":" + dep.name 43 | if os.path.exists(dep_dir): 44 | logger.info("reuse dependency {name} from {path}".format( 45 | name=qualified_name, path=dep_dir)) 46 | elif dep.name in dependency_paths: 47 | logger.info("link dependency {name} from {path}".format( 48 | name=qualified_name, path=dependency_paths[dep.name])) 49 | os.symlink(dependency_paths[dep.name], dep_dir) 50 | elif dep.url is not None: 51 | logger.info("fetch dependency {name} from {url}".format( 52 | name=qualified_name, url=dep.url)) 53 | import confu.git 54 | confu.git.clone(dep.url, dep_dir) 55 | elif dep.dir is not None: 56 | logger.info("link dependency {name} from {path}".format( 57 | name=qualified_name, path=os.path.join(project_dir, dep.dir))) 58 | os.symlink(os.path.join(project_dir, dep.dir), dep_dir) 59 | elif dep.name in builtin_recipes: 60 | logger.info("setup dependency {name} using built-in recipe confu.recipes.{name}" 61 | .format(name=qualified_name)) 62 | recipe = confu.recipes.__dict__[dep.name] 63 | recipe.setup(dep_dir) 64 | else: 65 | logger.critical("no source provided for dependency {name} ({qname})" 66 | .format(name=dep.name, qname=qualified_name)) 67 | 68 | setup_project_deps(dep_dir, root_dir, 69 | namespace=dep.name if not namespace else namespace + ":" + dep.name) 70 | 71 | import os 72 | root_dir = os.path.abspath(os.getcwd()) 73 | 74 | setup_project_deps(root_dir, root_dir) 75 | 76 | 77 | parser = argparse.ArgumentParser( 78 | description="Confu: cross-platform C/C++ configuration system") 79 | subparsers = parser.add_subparsers(title="commands", 80 | description="supported commands") 81 | setup_parser = subparsers.add_parser("setup", help="set up dependencies") 82 | setup_parser.add_argument("args", nargs=argparse.REMAINDER) 83 | setup_parser.set_defaults(process=setup_deps) 84 | 85 | 86 | def main(): 87 | options, unparsed_args = parser.parse_known_args() 88 | options.process(options, unparsed_args) 89 | 90 | 91 | if __name__ == "__main__": 92 | main() 93 | -------------------------------------------------------------------------------- /confu/arm/__init__.py: -------------------------------------------------------------------------------- 1 | from confu.arm.isa import v8, neon, fp16, fma, aes, sha, sha2, crc 2 | -------------------------------------------------------------------------------- /confu/arm/isa.py: -------------------------------------------------------------------------------- 1 | from confu.isa import InstructionSet 2 | 3 | 4 | def _generate_flags(tags, compiler): 5 | flags = [] 6 | is_armv8 = False 7 | is_crypto = any(crypto_feature in tags for crypto_feature in ["aes", "sha", "sha2"]) 8 | if is_crypto or any(armv8_feature in tags for armv8_feature in ["v8", "crc"]): 9 | is_armv8 = True 10 | flags.append("-march=armv8-a") 11 | elif any(armv7_feature in tags for armv7_feature in ["neon", "d32", "fp16", "fma"]): 12 | flags.append("-march=armv7-a") 13 | if is_crypto or "neon" in tags: 14 | if is_crypto: 15 | flags.append("-mfpu=crypto-neon-fp-armv8") 16 | flags.append("-mfp16-format=ieee") 17 | elif "v8" in tags: 18 | flags.append("-mfpu=neon-fp-armv8") 19 | flags.append("-mfp16-format=ieee") 20 | elif "fma" in tags: 21 | flags.append("-mfpu=neon-vfpv4") 22 | flags.append("-mfp16-format=ieee") 23 | elif "fp16" in tags: 24 | flags.append("-mfpu=neon-fp16") 25 | flags.append("-mfp16-format=ieee") 26 | else: 27 | flags.append("-mfpu=neon") 28 | elif "d32" in tags: 29 | if "v8" in tags: 30 | flags.append("-mfpu=fp-armv8") 31 | flags.append("-mfp16-format=ieee") 32 | elif "fma" in tags: 33 | flags.append("-mfpu=vfpv4") 34 | flags.append("-mfp16-format=ieee") 35 | elif "fp16" in tags: 36 | flags.append("-mfpu=vfpv3-fp16") 37 | flags.append("-mfp16-format=ieee") 38 | else: 39 | flags.append("-mfpu=vfpv3") 40 | elif "fma" in tags: 41 | flags.append("-mfpu=vfpv4-d16") 42 | flags.append("-mfp16-format=ieee") 43 | elif "fp16" in tags: 44 | flags.append("-mfpu=vfpv4-fp16") 45 | flags.append("-mfp16-format=ieee") 46 | return flags 47 | 48 | 49 | d32 = InstructionSet("d32", generate_flags_fn=_generate_flags) 50 | neon = InstructionSet("neon", generate_flags_fn=_generate_flags) 51 | fp16 = InstructionSet("fp16", generate_flags_fn=_generate_flags) 52 | fma = InstructionSet("fma", generate_flags_fn=_generate_flags) 53 | v8 = InstructionSet("v8", generate_flags_fn=_generate_flags) 54 | aes = InstructionSet("aes", generate_flags_fn=_generate_flags) 55 | sha = InstructionSet("sha", generate_flags_fn=_generate_flags) 56 | sha2 = InstructionSet("sha2", generate_flags_fn=_generate_flags) 57 | crc = InstructionSet("crc", generate_flags_fn=_generate_flags) 58 | 59 | -------------------------------------------------------------------------------- /confu/builds/__init__.py: -------------------------------------------------------------------------------- 1 | from confu.builds.base import Build 2 | from confu.builds.unix import UnixBuild 3 | from confu.builds.pnacl import PNaClBuild 4 | from confu.builds.emscripten import EmscriptenBuild 5 | from confu.builds.module import Module, ModuleCollection 6 | from confu.builds.deps import DependencyCollection 7 | -------------------------------------------------------------------------------- /confu/builds/base.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | 4 | import six 5 | 6 | logger = logging.getLogger("confu") 7 | 8 | from confu.builds.state import State 9 | 10 | 11 | class Build(State): 12 | def __init__(self, root_dir, target): 13 | super(Build, self).__init__(root_dir) 14 | if self.__class__ is Build: 15 | raise TypeError("This constructor is intended for use by subclasses only. " 16 | "Use static method Build.from_options to create an abstract Build class") 17 | import confu.platform 18 | assert isinstance(target, confu.platform.Platform) 19 | 20 | self.target = target 21 | 22 | from confu.manifest import Project 23 | self.manifest = Project.from_root(root_dir) 24 | logger.debug("parsed manifest for " + str(self.manifest)) 25 | 26 | from confu.builds import DependencyCollection 27 | self.deps = DependencyCollection(root_dir, self.manifest, self.target) 28 | 29 | from confu.builds import ModuleCollection 30 | self.modules = ModuleCollection() 31 | 32 | from confu.tools import ToolCollection 33 | self.tools = ToolCollection(self.target) 34 | 35 | @staticmethod 36 | def from_options(options, root_dir=None, **kwargs): 37 | from confu.utils import get_root_dir 38 | if root_dir is None: 39 | root_dir = get_root_dir() 40 | 41 | import confu.globals 42 | if confu.globals.root_dir is None: 43 | confu.globals.root_dir = root_dir 44 | logger.info("detected root directory: " + root_dir) 45 | 46 | from confu.platform import host 47 | if options.target.is_emscripten: 48 | from confu.builds import EmscriptenBuild 49 | return EmscriptenBuild(root_dir, options.target, options.toolchain) 50 | elif options.target == host or options.target.is_nacl or options.target.is_android: 51 | from confu.builds import UnixBuild, PNaClBuild 52 | if options.target.is_pnacl: 53 | return PNaClBuild(root_dir, options.target, options.toolchain) 54 | else: 55 | return UnixBuild(root_dir, options.target, options.toolchain) 56 | else: 57 | raise ValueError("Unsupported target platform {target}".format(target=options.target.name)) 58 | 59 | @property 60 | def active_module(self): 61 | return self.modules._active 62 | 63 | def cc(self, source_path): 64 | raise EnvironmentError("Compilation of C codes is not supported for {target}" 65 | .format(target=options.target.name)) 66 | 67 | def cxx(self, source_path): 68 | raise EnvironmentError("Compilation of C++ codes is not supported for {target}" 69 | .format(target=options.target.name)) 70 | 71 | def peachpy(self, source_path): 72 | raise EnvironmentError("Compilation of PeachPy codes is not supported for {target}" 73 | .format(target=options.target.name)) 74 | 75 | def export_cpath(self, include_dir, include_paths, add_to_include_dirs=True): 76 | from confu.validators import validate_include_dir 77 | include_dir = validate_include_dir(include_dir, self.root_dir) 78 | self.active_module.cpath.append(include_dir) 79 | if add_to_include_dirs: 80 | self._include_dirs.append(include_dir) 81 | 82 | def static_library(self, name, object_files): 83 | raise EnvironmentError("Static libraries are not supported for {target}" 84 | .format(target=options.target.name)) 85 | 86 | def dynamic_library(self, name, object_files): 87 | raise EnvironmentError("Dynamically loaded or shared libraries are not supported on {target}" 88 | .format(target=options.target.name)) 89 | 90 | def library(self, name, object_files): 91 | raise EnvironmentError("Function libraries are not supported for {target}" 92 | .format(target=options.target.name)) 93 | 94 | def plugin(self, name, object_files): 95 | raise EnvironmentError("Plugin modules are not supported on {target}" 96 | .format(target=options.target.name)) 97 | 98 | def executable(self, name, object_files): 99 | import confu.platform 100 | raise EnvironmentError("Executables are not supported on {target}" 101 | .format(target=options.target.name)) 102 | 103 | def unittest(self, name, object_files): 104 | import confu.platform 105 | if confu.platform.host == options.target: 106 | raise EnvironmentError("Unit tests are not supported on {target}" 107 | .format(target=options.target.name)) 108 | else: 109 | raise EnvironmentError("Unit tests are not supported in cross-compilation from {host} to {target}" 110 | .format(host=confu.platform.host, target=options.target.name)) 111 | 112 | def smoketest(self, name, object_files): 113 | import confu.platform 114 | if confu.platform.host == options.target: 115 | raise EnvironmentError("Smoke tests are not supported on {target}" 116 | .format(target=options.target.name)) 117 | else: 118 | raise EnvironmentError("Smoke tests are not supported in cross-compilation from {host} to {target}" 119 | .format(host=confu.platform.host, target=options.target.name)) 120 | 121 | def benchmark(self, name, object_files): 122 | import confu.platform 123 | if confu.platform.host == options.target: 124 | raise EnvironmentError("Benchmarks are not supported on {target}" 125 | .format(target=options.target.name)) 126 | else: 127 | raise EnvironmentError("Benchmarks are not supported in cross-compilation from {host} to {target}" 128 | .format(host=confu.platform.host, target=options.target.name)) 129 | 130 | def generate(self): 131 | import ninja_syntax 132 | import confu.globals 133 | confu.globals.build_ninja_path = os.path.join(self.root_dir, "build.ninja") 134 | with open(confu.globals.build_ninja_path, "w") as build_ninja: 135 | # Minimal version with implicit outputs support 136 | build_ninja.write("ninja_required_version = 1.7\n") 137 | 138 | ninja = ninja_syntax.Writer(build_ninja) 139 | self.generate_variables(ninja) 140 | self.generate_rules(ninja) 141 | 142 | import sys 143 | configure_path = os.path.abspath(os.path.normpath(sys.argv[0])) 144 | args = sys.argv[1:] 145 | ninja.rule("configure", configure_path + " $args", 146 | description="CONFIGURE $args", pool="console", generator=True) 147 | ninja.rule("clean", "ninja -f $config -t clean", 148 | description="CLEAN", pool="console") 149 | 150 | ninja.build("build.ninja", "configure", configure_path, 151 | variables={"args": " ".join(args)}) 152 | ninja.build("clean", "clean", 153 | variables={"config": confu.globals.build_ninja_path}) 154 | 155 | self.modules._record(ninja) 156 | 157 | def generate_variables(self, ninja): 158 | raise NotImplementedError() 159 | 160 | def generate_rules(self, ninja): 161 | raise NotImplementedError() 162 | -------------------------------------------------------------------------------- /confu/builds/deps.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import logging 4 | 5 | import six 6 | 7 | logger = logging.getLogger("confu") 8 | 9 | from confu.builds.state import State 10 | 11 | 12 | class DependencyCollection: 13 | def __init__(self, root_dir, manifest, target): 14 | from confu.manifest import Project 15 | assert isinstance(manifest, Project) 16 | 17 | import confu.globals 18 | self._deps = [dep.name for dep in manifest.deps] 19 | self._deps_dir = os.path.join(confu.globals.root_dir, "deps") 20 | self._target = target.name 21 | 22 | def __getattr__(self, name): 23 | if name not in self._deps: 24 | raise ValueError("Project manifest does not list dependency {name}".format(name=name)) 25 | 26 | import confu.globals 27 | if name in confu.globals.deps: 28 | return confu.globals.deps[name] 29 | 30 | logger.info("configuring dependency {name}".format(name=name)) 31 | 32 | import confu.recipes 33 | dep_dir = os.path.join(self._deps_dir, name) 34 | if os.path.isfile(os.path.join(dep_dir, "configure.py")): 35 | import sys 36 | sys.path.insert(0, dep_dir) 37 | try: 38 | import configure 39 | 40 | if sys.version_info >= (3, 4): 41 | from importlib import reload # Python 3.4+ 42 | elif sys.version_info >= (3, 0): 43 | from imp import reload # Python 3.0 - 3.3 44 | else: 45 | global reload 46 | 47 | reload(configure) 48 | config = configure.main(["--target", self._target]) 49 | config.modules._sealed = True 50 | confu.globals.deps[name] = config.modules 51 | return config.modules 52 | finally: 53 | sys.path = sys.path[1:] 54 | elif name in confu.recipes.__dict__: 55 | configure = confu.recipes.__dict__[name] 56 | config = configure.main(["--target", self._target], root_dir=dep_dir) 57 | config.modules._sealed = True 58 | confu.globals.deps[name] = config.modules 59 | return config.modules 60 | else: 61 | logger.fatal("don't know how to build {name}: configure.py not found in {path}" 62 | .format(name=name, path=dep_dir)) 63 | 64 | import errno 65 | raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), 66 | os.path.join(dep_dir, "configure.py")) 67 | 68 | 69 | -------------------------------------------------------------------------------- /confu/builds/emscripten.py: -------------------------------------------------------------------------------- 1 | import os 2 | import six 3 | 4 | from confu.builds import UnixBuild 5 | from confu.results import CompilationResult, CollectionResult 6 | 7 | 8 | class EmscriptenBuild(UnixBuild): 9 | def __init__(self, root_dir, target, toolchain): 10 | super(EmscriptenBuild, self).__init__(root_dir, target, toolchain) 11 | 12 | def _executable(self, name, object_files): 13 | if not isinstance(object_files, (list, tuple)): 14 | object_files = [object_files] 15 | emflags = [ 16 | "$emflags", 17 | "--memory-init-file", "0", 18 | "-s", "PRECISE_F32=2", 19 | ] 20 | if self.target.is_wasm: 21 | emflags += ["-s", "BINARYEN_METHOD=\\\"interpret-binary\\\""] 22 | executable = CollectionResult("bin", name, object_files, 23 | libraries=self._deps_libraries, filename=name + self.target.executable_ext, 24 | rule="executable", variables={ 25 | "linker": "$cxx", "emflags": " ".join(emflags)}) 26 | return executable 27 | 28 | def plugin(self, name, object_files, functions=None, memory_size=None, aborting_malloc=True, no_exit_runtime=True, memory_init_file=False, include_filesystem=None, 29 | pre_js=None, post_js=None): 30 | 31 | if not isinstance(object_files, (list, tuple)): 32 | object_files = [object_files] 33 | emflags = ["$emflags"] 34 | filename = name + self.target.executable_ext 35 | 36 | extra_outputs = list() 37 | if not memory_init_file: 38 | emflags += ["--memory-init-file", "0"] 39 | else: 40 | extra_outputs.append(filename + ".mem") 41 | 42 | if self.target.is_wasm: 43 | emflags += ["-s", "BINARYEN_METHOD=\\\"native-wasm\\\""] 44 | emflags += ["-s", "BINARYEN_IGNORE_IMPLICIT_TRAPS=1"] 45 | emflags += ["-s", "BINARYEN_TRAP_MODE=\\\"allow\\\""] 46 | extra_outputs.append(name + ".wasm") 47 | else: 48 | emflags += ["-s", "PRECISE_F32=2"] 49 | 50 | if functions is not None: 51 | from confu.validators import validate_export_functions 52 | functions = validate_export_functions(functions) 53 | 54 | emflags += ["-s", "EXPORTED_FUNCTIONS=\"[" + ",".join("'_%s'" % f for f in functions) + "]\""] 55 | 56 | if memory_size is not None: 57 | from confu.validators import validate_emscripten_memory_size 58 | memory_size_emflags = validate_emscripten_memory_size(memory_size) 59 | emflags += memory_size_emflags 60 | 61 | if memory_size is not all: 62 | emflags += ["-s", "ABORTING_MALLOC=" + str(int(bool(aborting_malloc)))] 63 | 64 | if no_exit_runtime: 65 | emflags += ["-s", "NO_EXIT_RUNTIME=1"] 66 | 67 | if include_filesystem is not None: 68 | if include_filesystem: 69 | emflags += ["-s", "FORCE_FILESYSTEM=1"] 70 | else: 71 | emflags += ["-s", "NO_FILESYSTEM=1"] 72 | 73 | extra_deps = list() 74 | if pre_js is not None: 75 | from confu.validators import validate_source_paths 76 | for js_path in validate_source_paths(pre_js, self.source_dir): 77 | extra_deps.append(js_path) 78 | emflags += ["--pre-js", js_path] 79 | if post_js is not None: 80 | from confu.validators import validate_source_paths 81 | for js_path in validate_source_paths(post_js, self.source_dir): 82 | extra_deps.append(js_path) 83 | emflags += ["--post-js", js_path] 84 | 85 | plugin = CollectionResult("out", name, object_files, 86 | libraries=self._deps_libraries, filename=filename, 87 | rule="executable", extra_outputs=extra_outputs, extra_deps=extra_deps, variables={ 88 | "linker": "$cxx", "emflags": " ".join(emflags)}) 89 | self.active_module.plugins.append(plugin) 90 | return plugin 91 | -------------------------------------------------------------------------------- /confu/builds/module.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import weakref 4 | import logging 5 | 6 | 7 | logger = logging.getLogger("confu") 8 | 9 | 10 | class ModuleCollection: 11 | def __init__(self): 12 | self._sealed = False 13 | 14 | default_module = Module("default", self) 15 | self.default = default_module 16 | self._active = default_module 17 | 18 | def __iter__(self): 19 | for name in dir(self): 20 | member = getattr(self, name) 21 | if name != "_active" and isinstance(member, Module): 22 | yield member 23 | 24 | def __str__(self): 25 | return "modules [" + ", ".join(member for member in dir(self) if isinstance(module, Module)) + "]" 26 | 27 | def __repr__(self): 28 | return str(self) 29 | 30 | def __getattr__(self, name): 31 | if name.startswith("__") and name.endswith("__"): 32 | # Query for built-in method, e.g. dir 33 | raise AttributeError() 34 | 35 | if self._sealed: 36 | raise AttributeError("Module {name} does not exist".format(name=name)) 37 | 38 | module = Module(name, self) 39 | setattr(self, name, module) 40 | return module 41 | 42 | def _record(self, ninja): 43 | defaults = sum([module._record(ninja) for module in self], list()) 44 | ninja.default(defaults) 45 | 46 | benchmarks, smoketests, tests = list(), list(), list() 47 | for module in self: 48 | benchmarks += [bench.name for bench in module.benchmarks] 49 | module_smoketests = [test.name for test in module.smoketests] 50 | smoketests += module_smoketests 51 | tests += module_smoketests 52 | tests += [test.name for test in module.unittests] 53 | 54 | if benchmarks: 55 | ninja.build("bench", "phony", benchmarks) 56 | if smoketests: 57 | ninja.build("smoketest", "phony", smoketests) 58 | if tests: 59 | ninja.build("test", "phony", tests) 60 | 61 | 62 | class Module: 63 | r"""Module is a container for build artifacts, and the minimal dependency that can be imported from a package. 64 | 65 | Modules contain artifacts produced by the package configuration script, e.g. 66 | 67 | - Exported CPATH (C/C++ include) directories and files 68 | - Libraries 69 | - Plug-in modules 70 | - Unit tests and smoke tests 71 | - Microbenchmarks 72 | - Other executables 73 | 74 | 75 | :type cpath: list of str 76 | :ivar cpath: collection of include directories. When the module is used as a dependency, these directories are 77 | added to the search path for C/C++ headers. 78 | 79 | :type executables: list of confu.results.CollectionResult 80 | :ivar executables: collection of executable programs produced by the configuration script. When the configuration 81 | script is invoked directly through command-line interface, these executables are built and copied 82 | into package's binary directory ("bin" by default). When the configuration script is invoked 83 | indirectly to configure a dependency package, these executables are not built. 84 | 85 | :type libraries: list of confu.results.CollectionResult 86 | :ivar libraries: collection of code libraries produced by the configuration script. When the configuration script is 87 | invoked directly through command-line interface, these libraries are built and copied into 88 | package's library directory ("lib" by default). When the configuration script is invoked 89 | indirectly to configure a dependency package, these libraries are built only if they are required 90 | to build an artifact of the top-level package. 91 | 92 | :type plugins: list of confu.results.CollectionResult 93 | :ivar plugins: collection of plugin modules produced by the configuration script. When the configuration script is 94 | invoked directly through command-line interface, these plugins are built and copied into package's 95 | output directory ("out" by default). When the configuration script is invoked indirectly to 96 | configure a dependency package, the plugins are not built. 97 | 98 | :type unittests: list of confu.results.CollectionResult 99 | :ivar unittests: collection of unit test produced by the configuration script. When the configuration script is 100 | invoked directly through command-line interface, these unit tests are built, copied into 101 | package's binary directory ("bin" by default), a target with the same name as the artifact is 102 | created to invoke the test, and a special "test" target is added to invoke all unit tests and smoke 103 | tests. When the configuration script is invoked indirectly to configure a dependency package, these 104 | unit tests are not built. 105 | 106 | :type smoketests: list of confu.results.CollectionResult 107 | :ivar smoketests: collection of smoke test produced by the configuration script. The only difference from unit tests 108 | is that a special target "smoketest" invokes all smoke tests, but not unit tests. Target "test" 109 | invokes both smoke tests and unit tests. 110 | 111 | :type benchmarks: list of confu.results.CollectionResult 112 | :ivar benchmarks: collection of benchmarks produced by the configuration script. When the configuration script is 113 | invoked directly through command-line interface, these benchmarks are built, copied into 114 | package's binary directory ("bin" by default), a target with the same name as the artifact is 115 | created to invoke the benchmark, and a special "bench" target is added to invoke all benchmarks. 116 | When the configuration script is invoked indirectly to configure a dependency package, these 117 | benchmarks are not built. 118 | """ 119 | 120 | def __init__(self, name, modules): 121 | from confu.validators import validate_module_name 122 | self._name = validate_module_name(name) 123 | self._modules = weakref.ref(modules) 124 | self._saved_active = None 125 | 126 | self.cpath = list() 127 | self.executables = list() 128 | self.plugins = list() 129 | self.libraries = list() 130 | self.unittests = list() 131 | self.smoketests = list() 132 | self.benchmarks = list() 133 | 134 | def __str__(self): 135 | return self._name 136 | 137 | def __repr__(self): 138 | return "module \"%s\"" % self._name 139 | 140 | def __enter__(self): 141 | modules = self._modules() 142 | self._saved_active = modules._active 143 | modules._active = self 144 | return self 145 | 146 | def __exit__(self, exc_type, exc_val, exc_tb): 147 | self._modules()._active = self._saved_active 148 | 149 | def _record(self, ninja): 150 | defaults = list() 151 | 152 | for library in self.libraries: 153 | target = library.generate(ninja) 154 | defaults.append(target) 155 | ninja.build(library.name, "phony", target) 156 | 157 | for plugin in self.plugins: 158 | target = plugin.generate(ninja) 159 | defaults.append(target) 160 | ninja.build(plugin.name, "phony", target) 161 | 162 | for executable in self.executables: 163 | target = executable.generate(ninja) 164 | defaults.append(target) 165 | ninja.build(executable.name, "phony", target) 166 | 167 | for benchmark in self.benchmarks: 168 | target = benchmark.generate(ninja) 169 | defaults.append(target) 170 | ninja.build(benchmark.name, "run", target, 171 | variables={"path": benchmark.name, "args": "--benchmark_color=true"}) 172 | 173 | for test in self.unittests + self.smoketests: 174 | target = test.generate(ninja) 175 | assert target is not None 176 | defaults.append(target) 177 | ninja.build(test.name, "run", target, 178 | variables={"path": test.name, "args": "--gtest_color=yes"}) 179 | 180 | artifacts = list() 181 | if self.libraries: 182 | artifacts.append("%d libraries" % len(self.libraries)) 183 | if self.plugins: 184 | artifacts.append("%d plugins" % len(self.plugins)) 185 | if self.executables: 186 | artifacts.append("%d executables" % len(self.executables)) 187 | if self.benchmarks: 188 | artifacts.append("%d benchmarks" % len(self.benchmarks)) 189 | if self.unittests or self.smoketests: 190 | if self.smoketests: 191 | artifacts.append("%d tests (%d smoke tests)" % 192 | (len(self.unittests) + len(self.smoketests), len(self.smoketests))) 193 | else: 194 | artifacts.append("%d tests" % len(self.unittests)) 195 | 196 | if artifacts: 197 | logger.info("Module %s: " % self._name + ", ".join(artifacts)) 198 | else: 199 | logger.info("Module %s: no artifacts" % self._name) 200 | 201 | return defaults 202 | -------------------------------------------------------------------------------- /confu/builds/pnacl.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from confu.builds import UnixBuild 4 | from confu.results import CompilationResult, CollectionResult 5 | 6 | 7 | class PNaClBuild(UnixBuild): 8 | def __init__(self, root_dir, target, toolchain): 9 | super(PNaClBuild, self).__init__(root_dir, target, toolchain) 10 | 11 | def _executable(self, name, object_files): 12 | if not isinstance(object_files, (list, tuple)): 13 | object_files = [object_files] 14 | linked_object = CollectionResult("build", name, object_files, 15 | libraries=self._deps_libraries, filename=name + self.target.get_object_ext(), 16 | rule="executable", variables={"linker": "$cxx"}) 17 | portable_executable_object = CollectionResult("build", name, [linked_object], 18 | filename=name + self.target.executable_ext, rule="finalize") 19 | native_executable_object = CollectionResult("bin", name, [portable_executable_object], 20 | filename=name + ".x86_64.nexe", rule="translate") 21 | return native_executable_object 22 | 23 | def plugin(self, name, object_files): 24 | if self.target.is_nacl: 25 | if not isinstance(object_files, (list, tuple)): 26 | object_files = [object_files] 27 | linked_object = CollectionResult("build", name, object_files, 28 | libraries=self._libs + self._deps_libraries, filename=name + self.target.get_object_ext(), 29 | rule="executable", variables={"linker": "$cxx"}) 30 | plugin_object = CollectionResult("out", name, [linked_object], 31 | filename=name + self.target.executable_ext, rule="finalize") 32 | self.active_module.plugins.append(plugin_object) 33 | return plugin_object 34 | else: 35 | super(UnixBuild, self).plugin(name, object_files) 36 | -------------------------------------------------------------------------------- /confu/builds/state.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import six 4 | 5 | from confu.isa import InstructionSet 6 | 7 | 8 | class OptionsContextManager: 9 | def __init__(self, state, source_dir=None, include_dirs=None, extra_include_dirs=None, 10 | macros=None, extra_macros=None, isa=None, deps=None, libs=None): 11 | 12 | assert isinstance(state, State) 13 | assert include_dirs is None or extra_include_dirs is None 14 | assert macros is None or extra_macros is None 15 | 16 | if not macros: 17 | macros = None 18 | elif isinstance(macros, str): 19 | macros = {macros: None} 20 | elif isinstance(macros, (list, tuple)): 21 | macros = {macro: None for macro in macros} 22 | 23 | if not extra_macros: 24 | extra_macros = None 25 | elif isinstance(extra_macros, str): 26 | extra_macros = {extra_macros: None} 27 | elif isinstance(extra_macros, (list, tuple)): 28 | extra_macros = {macro: None for macro in extra_macros} 29 | 30 | if isa is not None and not isinstance(isa, InstructionSet): 31 | raise TypeError("Invalid isa type; InstructionSet expected") 32 | 33 | if deps is not None and not isinstance(deps, list): 34 | deps = [deps] 35 | 36 | self.state = state 37 | self.source_dir = source_dir 38 | self.include_dirs = include_dirs 39 | self.extra_include_dirs = extra_include_dirs 40 | self.macros = macros 41 | self.extra_macros = extra_macros 42 | self.isa = isa 43 | self.deps = deps 44 | self.libs = libs 45 | 46 | self._saved_source_dir = None 47 | self._saved_include_dirs = None 48 | self._saved_macros = None 49 | self._saved_isa = None 50 | self._saved_deps = None 51 | self._saved_libs = None 52 | 53 | def __enter__(self): 54 | self._saved_source_dir = self.state._source_dir 55 | if self.source_dir is not None: 56 | self.state.source_dir = self.source_dir 57 | 58 | self._saved_include_dirs = self.state._include_dirs 59 | if self.include_dirs is not None: 60 | self.state.include_dirs = self.include_dirs 61 | elif self.extra_include_dirs is not None: 62 | for extra_include_dir in self.extra_include_dirs: 63 | self.state.add_include_dir(extra_include_dir) 64 | 65 | self._saved_macros = self.state._macros 66 | if self.macros is not None: 67 | self.state._macros = dict() 68 | for key, value in six.iteritems(self.macros): 69 | self.state.add_macro(key, value) 70 | elif self.extra_macros is not None: 71 | for key, value in six.iteritems(self.extra_macros): 72 | self.state.add_macro(key, value) 73 | 74 | self._saved_isa = self.state._isa 75 | if self.isa is not None: 76 | self.state._isa = self.isa 77 | 78 | self._saved_deps = self.state._deps 79 | if self.deps is not None: 80 | self.state._deps = self.deps 81 | 82 | self._saved_libs = self.state._libs 83 | if self.libs is not None: 84 | self.state._libs = self.libs 85 | 86 | return self.state 87 | 88 | def __exit__(self, exc_type, exc_val, exc_tb): 89 | self.state._source_dir = self._saved_source_dir 90 | self.state._include_dirs = self._saved_include_dirs 91 | self.state._isa = self._saved_isa 92 | self.state._deps = self._saved_deps 93 | self.state._libs = self._saved_libs 94 | 95 | 96 | class State(object): 97 | def __init__(self, root_dir): 98 | super(State, self).__init__() 99 | self._root_dir = root_dir 100 | 101 | self._macros = dict() 102 | self._source_dir = root_dir 103 | self._include_dirs = list() 104 | self._isa = InstructionSet() 105 | self._deps = list() 106 | self._libs = list() 107 | 108 | def options(self, source_dir=None, include_dirs=None, extra_include_dirs=None, 109 | macros=None, extra_macros=None, isa=None, deps=None, libs=None): 110 | 111 | if include_dirs is not None and extra_include_dirs is not None: 112 | raise ValueError("At most one of include_dirs, extra_include_dirs arguments can be provided") 113 | 114 | if macros is not None and extra_macros is not None: 115 | raise ValueError("At most one of macros, extra_macros arguments can be provided") 116 | 117 | if include_dirs is not None: 118 | from confu.validators import validate_include_dirs 119 | include_dirs = validate_include_dirs(include_dirs, self.root_dir) 120 | elif extra_include_dirs is not None: 121 | from confu.validators import validate_include_dirs 122 | extra_include_dirs = validate_include_dirs(extra_include_dirs, self.root_dir) 123 | 124 | if isa is not None: 125 | if not isinstance(isa, InstructionSet): 126 | raise TypeError("Invalid isa type; InstructionSet expected") 127 | 128 | if deps is not None: 129 | from confu.validators import validate_dependencies 130 | deps = validate_dependencies(deps, self) 131 | 132 | return OptionsContextManager(self, 133 | source_dir=source_dir, 134 | include_dirs=include_dirs, extra_include_dirs=extra_include_dirs, 135 | macros=macros, extra_macros=extra_macros, 136 | isa=isa, deps=deps, libs=libs) 137 | 138 | @property 139 | def root_dir(self): 140 | return self._root_dir 141 | 142 | @property 143 | def macros(self): 144 | return self._macros.copy() 145 | 146 | def add_macro(self, name, value=1): 147 | if name in self._macros: 148 | raise KeyError("Macro {name} is already defined as {value}".format(name, self._macros[value])) 149 | 150 | self._macros[name] = value 151 | 152 | def remove_macro(self, name): 153 | if name not in self._macros: 154 | raise KeyError("Macro {name} is not defined".format(name=name)) 155 | 156 | del self._macros[name] 157 | 158 | def clear_macros(self): 159 | self._macros = dict() 160 | 161 | @property 162 | def source_dir(self): 163 | return self._source_dir 164 | 165 | @source_dir.setter 166 | def source_dir(self, source_dir): 167 | from confu.validators import validate_source_dir 168 | self._source_dir = validate_source_dir(source_dir, self.root_dir) 169 | 170 | @property 171 | def include_dirs(self): 172 | return list(self._include_dirs) 173 | 174 | @include_dirs.setter 175 | def include_dirs(self, include_dirs): 176 | from confu.validators import validate_include_dirs 177 | self._include_dirs = validate_include_dirs(include_dirs, self.root_dir) 178 | 179 | def add_include_dir(self, include_dir): 180 | from confu.validators import validate_include_dir 181 | include_dir = validate_include_dir(include_dir, self.root_dir) 182 | self._include_dirs.append(include_dir) 183 | -------------------------------------------------------------------------------- /confu/builds/unix.py: -------------------------------------------------------------------------------- 1 | import os 2 | import six 3 | import collections 4 | 5 | from confu.builds import Build 6 | from confu.results import CompilationResult, CollectionResult 7 | 8 | 9 | class UnixBuild(Build): 10 | def __init__(self, root_dir, target, toolchain): 11 | super(UnixBuild, self).__init__(root_dir, target) 12 | if self.target.is_nacl: 13 | from confu.tools.toolchains import NaClToolchain 14 | self.toolchain = NaClToolchain(target, toolchain) 15 | elif self.target.is_emscripten: 16 | from confu.tools.toolchains import EmscriptenToolchain 17 | self.toolchain = EmscriptenToolchain(target, toolchain) 18 | elif self.target.is_android: 19 | from confu.tools.toolchains import AndroidToolchain 20 | self.toolchain = AndroidToolchain(target, toolchain) 21 | else: 22 | from confu.tools.toolchains import UnixToolchain 23 | self.toolchain = UnixToolchain(target) 24 | import confu.platform 25 | if confu.platform.host.is_linux and toolchain == "auto" or toolchain == "gnu": 26 | self.toolchain.cc = "gcc" 27 | self.toolchain.cxx = "g++" 28 | elif confu.platform.host.is_macos and toolchain == "auto" or toolchain == "clang": 29 | self.toolchain.cc = "clang" 30 | self.toolchain.cxx = "clang++" 31 | elif confu.platform.host.is_freebsd and toolchain == "auto" or toolchain == "clang": 32 | self.toolchain.cc = "clang" 33 | self.toolchain.cxx = "clang++" 34 | self.toolchain.ar = "ar" 35 | self.toolchain.ranlib = "ranlib" 36 | self.toolchain.strip = "strip" 37 | 38 | def generate_variables(self, ninja): 39 | import confu.globals 40 | ninja.variable("builddir", os.path.join(confu.globals.root_dir, "build")) 41 | ninja.variable("root", confu.globals.root_dir) 42 | 43 | self.toolchain.write_variables(ninja) 44 | for tool in six.itervalues(confu.globals.tools): 45 | tool._record_vars(ninja) 46 | 47 | def generate_rules(self, ninja): 48 | self.toolchain.write_rules(ninja) 49 | 50 | import confu.globals 51 | for tool in six.itervalues(confu.globals.tools): 52 | tool._record_rules(ninja) 53 | 54 | def _compile(self, rule, source_path): 55 | from confu.validators import validate_source_path 56 | source_path = validate_source_path(source_path, self.source_dir) 57 | variables = dict() 58 | include_dirs = self.include_dirs 59 | for dep in self._deps: 60 | if hasattr(dep, "cpath"): 61 | include_dirs += dep.cpath 62 | if include_dirs: 63 | variables["includes"] = "$includes " + " ".join("-I" + include_dir for include_dir in include_dirs) 64 | if self._isa: 65 | variables["optflags"] = "$optflags " + " ".join(self._isa.get_flags(self.toolchain.cc)) 66 | if self._macros: 67 | from confu.utils import format_macro 68 | variables["macro"] = " ".join(format_macro(name, self._macros[name]) for name in sorted(self._macros)) 69 | return CompilationResult(source_path, self.target, rule=rule, variables=variables) 70 | 71 | def cc(self, source_path): 72 | return self._compile("cc", source_path) 73 | 74 | def cxx(self, source_path): 75 | return self._compile("cxx", source_path) 76 | 77 | def peachpy(self, source_path): 78 | from confu.validators import validate_source_path 79 | source_path = validate_source_path(source_path, self.source_dir) 80 | 81 | include_dirs = sum((dep.cpath for dep in self._deps if hasattr(dep, "cpath")), self.include_dirs) 82 | return self.tools.peachpy.compile(source_path, include_dirs=include_dirs) 83 | 84 | @property 85 | def _deps_libraries(self): 86 | libraries = list() 87 | for dep in self._deps: 88 | if isinstance(dep, str): 89 | libraries.append(dep) 90 | else: 91 | libraries += dep.libraries 92 | return libraries 93 | 94 | def static_library(self, name, object_files): 95 | if not isinstance(object_files, collections.Iterable): 96 | object_files = [object_files] 97 | library = CollectionResult("lib", name, object_files, 98 | filename=self.target.get_static_library_filename(name), 99 | libraries=self._deps_libraries, rule="archive") 100 | self.active_module.libraries.append(library) 101 | return library 102 | 103 | def _executable(self, name, object_files): 104 | if not isinstance(object_files, collections.Iterable): 105 | object_files = [object_files] 106 | executable_object = CollectionResult("bin", name, object_files, 107 | filename=name + self.target.executable_ext, 108 | libraries=self._libs + self._deps_libraries, 109 | rule="executable", variables={"linker": "$cxx"}) 110 | return executable_object 111 | 112 | def plugin(self, name, object_files): 113 | if self.target.is_nacl: 114 | if not isinstance(object_files, collections.Iterable): 115 | object_files = [object_files] 116 | plugin_object = CollectionResult("out", name, object_files, 117 | filename=name + self.target.executable_ext, 118 | libraries=self._libs + self._deps_libraries, 119 | rule="executable", variables={"linker": "$cxx"}) 120 | self.active_module.plugins.append(plugin_object) 121 | return plugin_object 122 | else: 123 | super(UnixBuild, self).plugin(name, object_files) 124 | 125 | def executable(self, name, object_files): 126 | executable_object = self._executable(name, object_files) 127 | self.active_module.executables.append(executable_object) 128 | return executable_object 129 | 130 | def benchmark(self, name, object_files): 131 | executable_object = self._executable(name, object_files) 132 | self.active_module.benchmarks.append(executable_object) 133 | return executable_object 134 | 135 | def unittest(self, name, object_files): 136 | executable_object = self._executable(name, object_files) 137 | self.active_module.unittests.append(executable_object) 138 | return executable_object 139 | 140 | def smoketest(self, name, object_files): 141 | executable_object = self._executable(name, object_files) 142 | self.active_module.smoketests.append(executable_object) 143 | return executable_object 144 | -------------------------------------------------------------------------------- /confu/git.py: -------------------------------------------------------------------------------- 1 | import logging 2 | logger = logging.getLogger("confu") 3 | 4 | try: 5 | import pygit2 6 | if not(pygit2.features & pygit2.GIT_FEATURE_HTTPS) or not(pygit2.features & pygit2.GIT_FEATURE_SSH): 7 | logger.warning("pygit2 is built without HTTPS or SSH support, fall back to using git executable") 8 | pygit2 = None 9 | except ImportError: 10 | pygit2 = None 11 | 12 | 13 | failed_certificate_hosts = set() 14 | 15 | 16 | if pygit2 is not None: 17 | class RemoteCallbacks(pygit2.RemoteCallbacks): 18 | def __init__(self, credentials=None, certificate=None): 19 | super(RemoteCallbacks, self).__init__(credentials, certificate) 20 | 21 | def certificate_check(self, certificate, valid, host): 22 | if not valid: 23 | # Do not complain twice about the same host 24 | if host not in failed_certificate_hosts: 25 | logger.warning("could not validate certificate for {host}".format(host=host)) 26 | failed_certificate_hosts.add(host) 27 | 28 | return True 29 | else: 30 | class Repo: 31 | def __init__(self, root_dir): 32 | self.root_dir = root_dir 33 | 34 | @staticmethod 35 | def clone(url, path, checkout_branch=None): 36 | import subprocess 37 | args = ["git", "clone", "--quiet", url] 38 | if checkout_branch is not None: 39 | args += ["-b", checkout_branch] 40 | args.append(path) 41 | 42 | import os 43 | env = os.environ.copy() 44 | env["LC_ALL"] = "C" 45 | 46 | git = subprocess.Popen(args, env=env) 47 | git.communicate() 48 | assert git.returncode == 0 49 | 50 | return Repo(path) 51 | 52 | def checkout(self, refname): 53 | import subprocess 54 | args = ["git", "checkout", "--quiet", refname] 55 | 56 | import os 57 | env = os.environ.copy() 58 | env["LC_ALL"] = "C" 59 | 60 | git = subprocess.Popen(args, cwd=self.root_dir, env=env) 61 | git.communicate() 62 | assert git.returncode == 0 63 | 64 | 65 | def clone(url, path, checkout_branch=None): 66 | if pygit2 is not None: 67 | remote_callbacks = RemoteCallbacks() 68 | return pygit2.clone_repository(url, path, checkout_branch=checkout_branch, callbacks=remote_callbacks) 69 | else: 70 | return Repo.clone(url, path, checkout_branch=checkout_branch) 71 | -------------------------------------------------------------------------------- /confu/globals.py: -------------------------------------------------------------------------------- 1 | root_dir = None 2 | build_ninja_path = None 3 | deps = dict() 4 | tools = dict() 5 | -------------------------------------------------------------------------------- /confu/isa.py: -------------------------------------------------------------------------------- 1 | from copy import copy 2 | 3 | 4 | class InstructionSet: 5 | def __init__(self, tags=None, generate_flags_fn=None): 6 | if tags is None: 7 | self.tags = set() 8 | elif isinstance(tags, str): 9 | self.tags = set((tags,)) 10 | else: 11 | self.tags = set(tags) 12 | self.generate_flags = generate_flags_fn 13 | 14 | def get_flags(self, compiler): 15 | if self.generate_flags is not None: 16 | return self.generate_flags(self.tags, compiler) 17 | else: 18 | return list() 19 | 20 | def __str__(self): 21 | return self.name 22 | 23 | def __add__(self, instruction_set): 24 | if not isinstance(instruction_set, InstructionSet): 25 | raise TypeError("Invalid instruction set type; InstructionSet expected") 26 | 27 | if self.generate_flags is not None and self.generate_flags is not instruction_set.generate_flags: 28 | raise ValueError("Instruction sets %s and %s are mutually incompatible" % 29 | (self.tags[-1], instruction_set.tags[0])) 30 | 31 | return InstructionSet(self.tags.union(instruction_set.tags), self.generate_flags) 32 | -------------------------------------------------------------------------------- /confu/manifest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | 4 | 5 | logger = logging.getLogger("confu") 6 | 7 | 8 | class Dependency: 9 | def __init__(self, yaml_dict): 10 | self.name = yaml_dict["name"] 11 | self.url = yaml_dict.get("url") 12 | self.dir = yaml_dict.get("dir") 13 | 14 | 15 | class Project: 16 | def __init__(self, yaml_dict): 17 | self.name = yaml_dict["name"] 18 | self.title = yaml_dict.get("title") 19 | self.license = yaml_dict["license"] 20 | self.deps = [] 21 | if "deps" in yaml_dict: 22 | for dependency_yaml in yaml_dict["deps"]: 23 | self.deps.append(Dependency(dependency_yaml)) 24 | 25 | @staticmethod 26 | def from_root(root_dir): 27 | import yaml 28 | manifest_path = os.path.join(root_dir, "confu.yaml") 29 | try: 30 | with open(manifest_path) as manifest_file: 31 | manifest_text = manifest_file.read() 32 | except IOError as e: 33 | logger.critical("failed to read project manifest {path}: {message}".format( 34 | path=manifest_path, message=e.message)) 35 | raise 36 | manifest_yaml = yaml.load(manifest_text) 37 | try: 38 | return Project(manifest_yaml) 39 | except: 40 | logger.critical("invalid project manifest " + manifest_path) 41 | raise 42 | 43 | def __str__(self): 44 | if self.title: 45 | return self.name + ": " + self.title 46 | else: 47 | return self.name 48 | 49 | def __repr__(self): 50 | if self.deps: 51 | return str(self) + " (deps: " + " ".join(dep.name for dep in self.deps) + ")" 52 | else: 53 | return str(self) 54 | -------------------------------------------------------------------------------- /confu/platform.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | 4 | import os 5 | import sys 6 | import logging 7 | 8 | 9 | logger = logging.getLogger("confu") 10 | 11 | 12 | class Platform: 13 | r"""A combination of processor architecture, operating system (or runtime environment), and optionally ABI and 14 | C library. 15 | 16 | This class identifies host (where the code is compiled) and target (where the code will run) systems, and 17 | encapsulates information about the system, which doesn't depend on toolchains or installed packages, e.g. 18 | 19 | - Processor architecture and basic instruction set 20 | - Target operating system or runtime environment (e.g. Emscripten) 21 | - Application binary interface (e.g. hard-float vs soft-float) 22 | - Type of C library (e.g. GNU LibC, Newlib) 23 | - Extension of object and executable files on the system (e.g. module.o, module.bc, module.obj) 24 | - Static library naming conventions on the system (e.g. libcalc.a, libcalc.la, calcs.lib) 25 | - Dynamic library naming conventions on the system (e.g. libcalc.so, libcalc.dylib, calc.dll) 26 | 27 | :type name: str 28 | :ivar name: normalized identifier of the platform. Identifier has two or three dash-separated parts: the first 29 | denotes CPU architecture, the second -- operating system or runtime environment. The third, optional, 30 | part denotes the type of C library and ABI. Examples: 31 | 32 | - "x86_64-linux-gnu" 33 | - "x86_64-linux-gnux32" 34 | - "ppc64le-linux-gnu" 35 | - "arm-linux-gnueabihf" 36 | - "aarch64-linux-gnu" 37 | - "x86_64-macos" 38 | - "arm-android-v7a" 39 | - "aarch64-android-v8a" 40 | - "x86-android" 41 | - "x86_64-android" 42 | - "x86_64-nacl-gnu" 43 | - "x86_64-nacl-newlib" 44 | - "pnacl-nacl-newlib" 45 | - "asmjs-emscripten" 46 | - "wasm32-emscripten" 47 | """ 48 | 49 | def __init__(self, name): 50 | r"""Platform object is created from an informal name identifier. 51 | 52 | :param str name: identifier for a platform. Examples: 53 | 54 | - "x86_64-linux" (same as "x86_64-linux-gnu") 55 | - "ppc64le-linux" (same as "ppc64le-linux-gnu") 56 | - "arm-linux" (same as "arm-linux-gnueabihf") 57 | - "arm64-linux" (same as "aarch64-linux-gnu") 58 | - "aarch64-linux" (same as "aarch64-linux-gnu") 59 | - "x32" (same as "x86_64-linux-gnux32") 60 | - "arm-android" (same as "arm-android-v7a") 61 | - "aarch64-android" (same as "aarch64-android-v8a") 62 | - "x86_64-nacl" (same as "x86_64-nacl-newlib") 63 | - "pnacl" (same as "pnacl-nacl-newlib") 64 | - "asmjs" (same as "asmjs-emscripten") 65 | - "wasm" (same as "wasm32-emscripten") 66 | """ 67 | 68 | from confu.validators import validate_platform_name 69 | 70 | self.name = validate_platform_name(name) 71 | parts = self.name.split("-") 72 | self.arch, self.os = parts[0:2] 73 | 74 | def __str__(self): 75 | return "platform '" + self.name + "'" 76 | 77 | def __repr__(self): 78 | return "platform '" + self.name + "'" 79 | 80 | def __hash__(self): 81 | return hash(self.name) 82 | 83 | def __eq__(self, other): 84 | from confu.validators import validate_platform_name 85 | try: 86 | return self.name == validate_platform_name(other) 87 | except: 88 | return isinstance(other, Platform) and self.name == other.name 89 | 90 | def __ne__(self, other): 91 | return not(self == other) 92 | 93 | def get_static_library_filename(self, name, pic=False): 94 | if self.is_pnacl or self.is_emscripten: 95 | assert not pic 96 | return "lib" + name + ".a" 97 | elif self.is_windows: 98 | assert not pic 99 | return name + "s.lib" 100 | else: 101 | return "lib" + name + (".la" if pic else ".a") 102 | 103 | def get_dynamic_library_filename(self, name, pic=False): 104 | if self.is_macos: 105 | return "lib" + name + ".dylib" 106 | elif self.is_windows: 107 | return name + ".dll" 108 | else: 109 | return "lib" + name + ".so" 110 | 111 | def get_object_ext(self, pic=False): 112 | if self.is_pnacl or self.is_emscripten: 113 | assert not pic 114 | return ".bc" 115 | elif self.is_windows: 116 | assert not pic 117 | return ".obj" 118 | else: 119 | return ".lo" if pic else ".o" 120 | 121 | @property 122 | def executable_ext(self): 123 | if self.is_pnacl: 124 | return ".pexe" 125 | elif self.is_nacl: 126 | assert self.is_x86_64 127 | return ".x86_64.nexe" 128 | elif self.is_asmjs: 129 | return ".asm.js" 130 | elif self.is_wasm: 131 | return ".js" 132 | elif self.is_windows: 133 | return ".exe" 134 | else: 135 | return "" 136 | 137 | @property 138 | def is_x86(self): 139 | return self.arch == "x86" 140 | 141 | @property 142 | def is_x86_64(self): 143 | return self.arch in ["x86_64", "amd64"] 144 | 145 | @property 146 | def is_ppc64(self): 147 | return self.arch in ["ppc64", "ppc64le"] 148 | 149 | @property 150 | def is_arm(self): 151 | return self.arch == "arm" 152 | 153 | @property 154 | def is_arm64(self): 155 | return self.arch in ["arm64", "aarch64"] 156 | 157 | @property 158 | def is_pnacl(self): 159 | return self.arch == "pnacl" 160 | 161 | @property 162 | def is_asmjs(self): 163 | return self.arch == "asmjs" 164 | 165 | @property 166 | def is_wasm(self): 167 | return self.arch == "wasm32" 168 | 169 | @property 170 | def is_linux(self): 171 | return self.os == "linux" 172 | 173 | @property 174 | def is_android(self): 175 | return self.os == "android" 176 | 177 | @property 178 | def is_freebsd(self): 179 | return self.os == "freebsd" 180 | 181 | @property 182 | def is_macos(self): 183 | return self.os == "macos" 184 | 185 | @property 186 | def is_windows(self): 187 | return self.os == "windows" 188 | 189 | @property 190 | def is_nacl(self): 191 | return self.os == "nacl" 192 | 193 | @property 194 | def is_emscripten(self): 195 | return self.os == "emscripten" 196 | 197 | @property 198 | def is_web(self): 199 | return self.is_nacl or self.is_emscripten 200 | 201 | @property 202 | def is_newlib(self): 203 | return self.name.endswith("-newlib") 204 | 205 | @property 206 | def is_glibc(self): 207 | parts = self.name.split("-") 208 | return len(parts) >= 3 and parts[2].startswith("gnu") 209 | 210 | 211 | def detect_host(): 212 | x86_64_machines = ["x86_64", "amd64"] 213 | x86_machines = ["i386", "i686"] 214 | arm_machines = ["armv6l", "armv7l"] 215 | arm64_machines = ["aarch64"] 216 | import platform 217 | machine = platform.machine() 218 | if sys.platform.startswith("linux"): 219 | if machine in x86_64_machines: 220 | return "x86_64-linux-gnu" 221 | elif machine in x86_machines: 222 | return "x86-linux-gnu" 223 | elif machine in arm_machines: 224 | return "arm-linux-gnueabihf" 225 | elif machine in arm64_machines: 226 | return "aarch64-linux-gnu" 227 | elif machine == "ppc64le": 228 | return "ppc64le-linux-gnu" 229 | elif sys.platform == "darwin": 230 | if machine in x86_64_machines: 231 | return "x86_64-macos" 232 | elif sys.platform == "win32": 233 | if machine in x86_64_machines: 234 | return "x86_64-windows" 235 | elif sys.platform.startswith("freebsd"): 236 | if machine in x86_64_machines: 237 | return "x86_64-freebsd" 238 | else: 239 | logging.critical("failed to detect the host platform: " 240 | "sys.platform = {sys.platform}".format(sys=sys)) 241 | raise ValueError("Unexpected sys.platform value ({sys.platform})".format(sys=sys)) 242 | 243 | logging.critical("failed to detect the host platform: " 244 | "sys.platform = {sys.platform}, platform.machine = {machine}" 245 | .format(sys=sys, machine=machine)) 246 | raise ValueError("Unexpected sys.platform, platform.machine combination ({sys.platform}, {machine})" 247 | .format(sys=sys, machine=machine)) 248 | 249 | host = None 250 | if host is None: 251 | host = Platform(detect_host()) 252 | logger.debug("host platform: " + host.name) 253 | 254 | 255 | def detect_possible_targets(): 256 | targets = [host.name] 257 | if os.getenv("ANDROID_SDK") is not None and os.getenv("ANDROID_NDK") is not None: 258 | targets.append("arm-android") 259 | targets.append("arm-android-v7a") 260 | targets.append("aarch64-android") 261 | targets.append("aarch64-android-v8a") 262 | targets.append("x86-android") 263 | targets.append("x86_64-android") 264 | if host.is_x86_64: 265 | if os.getenv("NACL_SDK_ROOT") is not None: 266 | targets.append("pnacl") 267 | targets.append("x86_64-nacl") 268 | targets.append("x86_64-nacl-gnu") 269 | if os.path.isfile(os.path.expanduser("~/.emscripten")) or os.getenv("EMSCRIPTEN") is not None: 270 | targets.append("asmjs") 271 | targets.append("wasm") 272 | return targets 273 | 274 | possible_targets = None 275 | if possible_targets is None: 276 | possible_targets = detect_possible_targets() 277 | logger.debug("possible target platforms:" + os.linesep + os.linesep.join("\t" + target for target in possible_targets)) 278 | -------------------------------------------------------------------------------- /confu/recipes/__init__.py: -------------------------------------------------------------------------------- 1 | from confu.recipes import googletest, googlebenchmark 2 | -------------------------------------------------------------------------------- /confu/recipes/googlebenchmark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | def setup(root_dir): 5 | import confu.git 6 | repo = confu.git.clone("https://github.com/google/benchmark.git", root_dir) 7 | 8 | from os import path 9 | recipes_dir = path.dirname(path.abspath(__file__)) 10 | 11 | import shutil 12 | shutil.copyfile( 13 | path.join(recipes_dir, "googlebenchmark.yaml"), 14 | path.join(root_dir, "confu.yaml")) 15 | 16 | 17 | def main(args, root_dir=None): 18 | import confu 19 | options = confu.standard_parser("Google micro-Benchmark framework configuration script").parse_args(args) 20 | build = confu.Build.from_options(options, root_dir=root_dir) 21 | 22 | build.export_cpath("include", ["benchmark/*.h"]) 23 | 24 | source_files = [ 25 | "benchmark.cc", 26 | "benchmark_api_internal.cc", 27 | "benchmark_main.cc", 28 | "benchmark_register.cc", 29 | "benchmark_runner.cc", 30 | "colorprint.cc", 31 | "commandlineflags.cc", 32 | "complexity.cc", 33 | "console_reporter.cc", 34 | "counter.cc", 35 | "csv_reporter.cc", 36 | "json_reporter.cc", 37 | "reporter.cc", 38 | "sleep.cc", 39 | "statistics.cc", 40 | "string_util.cc", 41 | "sysinfo.cc", 42 | "timers.cc", 43 | ] 44 | 45 | macros = [ 46 | "HAVE_POSIX_REGEX", 47 | "NDEBUG", 48 | ] 49 | with build.options(source_dir="src", macros=macros, extra_include_dirs="src"): 50 | build.static_library("googlebenchmark", 51 | [build.cxx(source) for source in source_files]) 52 | 53 | return build 54 | -------------------------------------------------------------------------------- /confu/recipes/googlebenchmark.yaml: -------------------------------------------------------------------------------- 1 | name: googlebenchmark 2 | title: Google micro-Benchmark framework 3 | license: Apache 2.0 4 | platforms: 5 | unsupported: 6 | - x86_64-nacl-gnu 7 | - pnacl-nacl-newlib 8 | - wasm32-emscripten 9 | - asmjs-emscripten 10 | -------------------------------------------------------------------------------- /confu/recipes/googletest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | def setup(root_dir): 5 | import confu.git 6 | repo = confu.git.clone("https://github.com/google/googletest.git", root_dir) 7 | repo.checkout("refs/tags/release-1.10.0") 8 | 9 | from os import path 10 | recipes_dir = path.dirname(path.abspath(__file__)) 11 | 12 | import shutil 13 | shutil.copyfile( 14 | path.join(recipes_dir, "googletest.yaml"), 15 | path.join(root_dir, "confu.yaml")) 16 | 17 | 18 | def main(args, root_dir=None): 19 | import confu 20 | options = confu.standard_parser("Google Test framework configuration script").parse_args(args) 21 | build = confu.Build.from_options(options, root_dir=root_dir) 22 | 23 | with build.options(source_dir="googletest/src", include_dirs=["googletest/include", "googletest"]): 24 | gtest_object = build.cxx("gtest-all.cc") 25 | 26 | with build.modules.default: 27 | build.export_cpath("googletest/include", ["gtest/**/*.h"]) 28 | build.static_library("googletest", 29 | [gtest_object, build.cxx("gtest_main.cc")]) 30 | 31 | with build.modules.core: 32 | build.export_cpath("googletest/include", ["gtest/**/*.h"]) 33 | build.static_library("googletest-core", gtest_object) 34 | 35 | return build 36 | -------------------------------------------------------------------------------- /confu/recipes/googletest.yaml: -------------------------------------------------------------------------------- 1 | name: googletest 2 | title: Google Test framework 3 | license: New BSD 4 | -------------------------------------------------------------------------------- /confu/results/__init__.py: -------------------------------------------------------------------------------- 1 | from confu.results.build import BuildResult 2 | from confu.results.compilcation import CompilationResult 3 | from confu.results.collection import CollectionResult 4 | -------------------------------------------------------------------------------- /confu/results/build.py: -------------------------------------------------------------------------------- 1 | class BuildResult(object): 2 | def __init__(self): 3 | super(BuildResult, self).__init__() 4 | self.generated = False 5 | 6 | def get_object_path(self, root_dir): 7 | raise NotImplementedError() 8 | 9 | def generate(self, ninja, root_dir): 10 | raise NotImplementedError() 11 | -------------------------------------------------------------------------------- /confu/results/collection.py: -------------------------------------------------------------------------------- 1 | import os 2 | import collections 3 | 4 | from confu.results import BuildResult 5 | from confu.results import CompilationResult 6 | from confu.utils import qualified_type 7 | 8 | 9 | class CollectionResult(BuildResult): 10 | def __init__(self, subdir, name, objects, libraries=None, filename=None, rule=None, extra_outputs=list(), extra_deps=list(), variables=dict()): 11 | super(CollectionResult, self).__init__() 12 | if not isinstance(subdir, str): 13 | raise TypeError("Unsupported type of subdir argument: string expected") 14 | if subdir not in ['bin', 'lib', 'out', 'build']: 15 | raise ValueError("Unsupported value {subdir} of subdir argument: 'bin', 'lib', 'out', or 'build' expected".format( 16 | subdir=subdir)) 17 | if not isinstance(name, str): 18 | raise TypeError("Unsupported type of name argument: string expected") 19 | if not isinstance(objects, collections.Iterable): 20 | raise TypeError("Unsupported type of objects argument: list, tuple, or iterable expected") 21 | if not isinstance(objects, list): 22 | objects = list(objects) 23 | for i, obj in enumerate(objects): 24 | if not isinstance(obj, (CompilationResult, CollectionResult)): 25 | raise TypeError("Unsupported type {type} of object #{index}: CompilationResult expected".format( 26 | type=qualified_type(obj), index=i)) 27 | self.subdir = subdir 28 | self.name = name 29 | self.filename = filename if filename is not None else name 30 | self.objects = objects 31 | self.libraries = libraries 32 | self.rule = rule 33 | self.extra_outputs = extra_outputs 34 | self.extra_deps = extra_deps 35 | self.variables = variables 36 | 37 | def get_target_path(self): 38 | import confu.globals 39 | return os.path.join(confu.globals.root_dir, self.subdir, self.filename) 40 | 41 | def generate(self, ninja): 42 | import confu.globals 43 | if self.generated: 44 | return self.get_target_path() 45 | 46 | object_files = list() 47 | for object in self.objects: 48 | assert isinstance(object, (CompilationResult, CollectionResult)) 49 | object.generate(ninja) 50 | if isinstance(object, CompilationResult): 51 | object_files.append(object.get_object_path()) 52 | else: 53 | object_files.append(object.get_target_path()) 54 | 55 | library_files = list() 56 | implicit_deps = list(self.extra_deps) 57 | if self.libraries: 58 | for library in self.libraries: 59 | assert isinstance(library, (CollectionResult, str)) 60 | if isinstance(library, str): 61 | library_files.append("-l" + library) 62 | else: 63 | library.generate(ninja) 64 | library_files.append(library.get_target_path()) 65 | implicit_deps.append(library.get_target_path()) 66 | 67 | target_path = self.get_target_path() 68 | variables = self.variables.copy() 69 | variables["path"] = os.path.join(self.subdir, self.filename) 70 | variables["ldlibs"] = " ".join(library_files + ["$ldlibs"]) 71 | ninja.build(target_path, self.rule, object_files, 72 | implicit=implicit_deps, 73 | implicit_outputs=[os.path.join(confu.globals.root_dir, self.subdir, extra_output) 74 | for extra_output in self.extra_outputs], 75 | order_only=confu.globals.build_ninja_path, variables=variables) 76 | 77 | self.generated = True 78 | return target_path 79 | -------------------------------------------------------------------------------- /confu/results/compilcation.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from confu.results.build import BuildResult 4 | 5 | 6 | class CompilationResult(BuildResult): 7 | def __init__(self, source_file, target_platform, rule=None, variables=dict()): 8 | super(CompilationResult, self).__init__() 9 | self.target_platform = target_platform 10 | self.source_file = source_file 11 | self.rule = rule 12 | self.variables = variables 13 | 14 | def get_object_path(self): 15 | import confu.globals 16 | rel_source_file = os.path.relpath(self.source_file, confu.globals.root_dir) 17 | return os.path.join(confu.globals.root_dir, "build", rel_source_file) + self.target_platform.get_object_ext() 18 | 19 | def generate(self, ninja): 20 | if self.generated: 21 | return 22 | 23 | import confu.globals 24 | object_file = self.get_object_path() 25 | variables = self.variables.copy() 26 | variables["path"] = os.path.relpath(self.source_file, confu.globals.root_dir) 27 | ninja.build(object_file, self.rule, self.source_file, 28 | order_only=confu.globals.build_ninja_path, variables=variables) 29 | 30 | self.generated = True 31 | -------------------------------------------------------------------------------- /confu/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | 4 | from confu.tools.base import Tool 5 | from confu.tools.collection import ToolCollection 6 | from confu.tools.peachpy import PeachPy 7 | -------------------------------------------------------------------------------- /confu/tools/base.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | 4 | class Tool(object): 5 | def __init__(self, target): 6 | super(Tool, self).__init__() 7 | self.target = target 8 | 9 | @staticmethod 10 | def for_name(name, target): 11 | if name == "peachpy": 12 | from confu.tools import PeachPy 13 | return PeachPy(target) 14 | else: 15 | raise ValueError("Tool {name} not available: name not recognized".format(name=name)) 16 | 17 | def _record_vars(self, ninja): 18 | pass 19 | 20 | def _record_rules(self, ninja): 21 | pass 22 | -------------------------------------------------------------------------------- /confu/tools/collection.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import logging 4 | 5 | 6 | logger = logging.getLogger("confu") 7 | 8 | 9 | class ToolCollection: 10 | def __init__(self, target): 11 | self.target = target 12 | 13 | def __iter__(self): 14 | from confu.tools import Tool 15 | for name in dir(self): 16 | member = getattr(self, name) 17 | if isinstance(member, Tool): 18 | yield member 19 | 20 | def __str__(self): 21 | return "toolset [" + ", ".join(str(tool) for tool in self) + "]" 22 | 23 | def __repr__(self): 24 | return str(self) 25 | 26 | def __getattr__(self, name): 27 | if name.startswith("__") and name.endswith("__"): 28 | # Query for built-in method, e.g. dir 29 | raise AttributeError() 30 | 31 | import confu.globals 32 | if name in confu.globals.tools: 33 | return confu.globals.tools[name] 34 | 35 | from confu.tools import Tool 36 | tool = Tool.for_name(name, self.target) 37 | confu.globals.tools[name] = tool 38 | setattr(self, name, tool) 39 | return tool 40 | 41 | def _record_vars(self, ninja): 42 | for tool in self: 43 | tool._record_vars(ninja) 44 | 45 | def _record_rules(self, ninja): 46 | for tool in self: 47 | tool._record_rules(ninja) 48 | -------------------------------------------------------------------------------- /confu/tools/peachpy.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from confu.tools import Tool 4 | 5 | 6 | class PeachPy(Tool): 7 | def __init__(self, target): 8 | super(PeachPy, self).__init__(target) 9 | 10 | if self.target.is_x86_64: 11 | try: 12 | import peachpy.x86_64 13 | except ImportError: 14 | raise EnvironmentError("PeachPy compiler is not installed on this platform") 15 | else: 16 | raise EnvironmentError("PeachPy compiler does not support {target} platform".format(target=target.name)) 17 | 18 | def compile(self, source_path, include_dirs): 19 | variables = dict() 20 | if include_dirs: 21 | variables["includes"] = "$includes " + " ".join("-I" + include_dir for include_dir in include_dirs) 22 | 23 | from confu.results import CompilationResult 24 | return CompilationResult(source_path, self.target, rule="peachpy", variables=variables) 25 | 26 | def _record_vars(self, ninja): 27 | ninja.variable("peachpy", "python -m peachpy.x86_64") 28 | 29 | def _record_rules(self, ninja): 30 | abi, imageformat = { 31 | "x86_64-linux-gnu": ("sysv", "elf"), 32 | "x86_64-freebsd": ("sysv", "elf"), 33 | "x86_64-macos": ("sysv", "mach-o"), 34 | "x86_64-nacl-newlib": ("nacl", "elf"), 35 | "x86_64-nacl-gnu": ("nacl", "elf"), 36 | }[self.target] 37 | ninja.rule("peachpy", 38 | "$peachpy -mabi={abi} -g4 -mimage-format={imageformat} $includes -MMD -MF $out.d -o $out $in" 39 | .format(abi=abi, imageformat=imageformat), 40 | deps="gcc", depfile="$out.d", 41 | description="PEACHPY $path") 42 | -------------------------------------------------------------------------------- /confu/tools/toolchains/__init__.py: -------------------------------------------------------------------------------- 1 | from confu.tools.toolchains.base import Toolchain 2 | from confu.tools.toolchains.unix import UnixToolchain 3 | from confu.tools.toolchains.nacl import NaClToolchain 4 | from confu.tools.toolchains.android import AndroidToolchain 5 | from confu.tools.toolchains.emscripten import EmscriptenToolchain 6 | -------------------------------------------------------------------------------- /confu/tools/toolchains/android.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from confu.platform import Platform 4 | from confu.tools.toolchains import UnixToolchain 5 | import confu.globals 6 | import confu.platform 7 | 8 | import os 9 | import logging 10 | 11 | logger = logging.getLogger("confu") 12 | 13 | 14 | class AndroidToolchain(UnixToolchain): 15 | def __init__(self, target, toolchain): 16 | super(AndroidToolchain, self).__init__(target) 17 | assert isinstance(target, Platform) 18 | assert target.is_android 19 | assert isinstance(toolchain, str) 20 | 21 | if toolchain == "auto": 22 | toolchain = "clang" 23 | assert toolchain in ["gnu", "clang"] 24 | 25 | android_ndk_root = os.getenv("ANDROID_NDK") 26 | android_sdk_root = os.getenv("ANDROID_SDK") 27 | logger.debug("Android SDK path: {sdk}".format(sdk=android_sdk_root)) 28 | logger.debug("Android NDK path: {ndk}".format(ndk=android_ndk_root)) 29 | 30 | self.sdk_root = android_sdk_root 31 | self.ndk_root = android_ndk_root 32 | 33 | self.sysroot = os.path.join(self.ndk_root, "sysroot") 34 | 35 | android_abi, sysroot_abi, toolchain_name, toolchain_root, header_triple, llvm_triple = { 36 | "arm-android-v7a": ("armeabi-v7a", "arm", "arm-linux-androideabi", "arm-linux-androideabi", "arm-linux-androideabi", "armv7-none-linux-androideabi"), 37 | "aarch64-android-v8a": ("arm64-v8a", "arm64", "aarch64-linux-android", "aarch64-linux-android", "aarch64-linux-android", "aarch64-none-linux-android"), 38 | "x86-android": ("x86", "x86", "i686-linux-android", "x86", "i686-linux-android", "i686-none-linux-android"), 39 | "x86_64-android": ("x86_64", "x86_64", "x86_64-linux-android", "x86_64", "x86_64-linux-android", "x86_64-none-linux-android"), 40 | }[target] 41 | platform_level = 14 42 | if target.is_arm64 or target.is_x86_64: 43 | platform_level = 21 44 | 45 | system_library_path = os.path.join(self.ndk_root, "platforms", "android-%d" % platform_level, "arch-" + sysroot_abi) 46 | 47 | self.cflags += [ 48 | "--sysroot", self.sysroot, 49 | "-isystem", os.path.join(self.sysroot, "usr", "include", header_triple) 50 | ] 51 | self.cxxflags += [ 52 | "--sysroot", self.sysroot, 53 | "-isystem", os.path.join(self.sysroot, "usr", "include", header_triple) 54 | ] 55 | self.cflags += ["-D__ANDROID_API__={platform_level}".format(platform_level=platform_level)] 56 | self.cxxflags += ["-D__ANDROID_API__={platform_level}".format(platform_level=platform_level)] 57 | self.ldflags += ["--sysroot", system_library_path] 58 | self.ldflags += ["-L", os.path.join(system_library_path, "usr", "lib")] 59 | 60 | # Don't re-export libgcc symbols in every binary. 61 | self.ldflags += ["-Wl,--exclude-libs,libgcc.a"] 62 | 63 | # Toolchain. 64 | host_tag = { 65 | "x86_64-linux-gnu": "linux-x86_64", 66 | "x86_64-macos": "darwin-x86_64", 67 | "x86_64-windows": "windows-x86_64", 68 | }[confu.platform.host] 69 | 70 | toolchain_root = os.path.join(self.ndk_root, "toolchains", toolchain_root + "-4.9", "prebuilt", host_tag) 71 | toolchain_prefix = os.path.join(toolchain_root, "bin") 72 | toolchain_suffix = ".exe" if confu.platform.host.is_windows else "" 73 | if toolchain == "gnu": 74 | self.cc = os.path.join(toolchain_prefix, toolchain_name + "-gcc" + toolchain_suffix) 75 | self.cxx = os.path.join(toolchain_prefix, toolchain_name + "-g++" + toolchain_suffix) 76 | self.ar = os.path.join(toolchain_prefix, toolchain_name + "-gcc-ar" + toolchain_suffix) 77 | self.ranlib = os.path.join(toolchain_prefix, toolchain_name + "-gcc-ranlib" + toolchain_suffix) 78 | elif toolchain == "clang": 79 | llvm_toolchain_prefix = os.path.join(self.ndk_root, "toolchains", "llvm", "prebuilt", host_tag, "bin") 80 | self.cc = os.path.join(llvm_toolchain_prefix, "clang" + toolchain_suffix) 81 | self.cxx = os.path.join(llvm_toolchain_prefix, "clang++" + toolchain_suffix) 82 | self.cflags = ["-target", llvm_triple, "-gcc-toolchain", toolchain_root] + self.cflags 83 | self.cxxflags = ["-target", llvm_triple, "-gcc-toolchain", toolchain_root] + self.cxxflags 84 | self.ldflags = ["-target", llvm_triple, "-gcc-toolchain", toolchain_root] + self.ldflags 85 | 86 | self.ar = os.path.join(toolchain_prefix, toolchain_name + "-ar" + toolchain_suffix) 87 | self.ranlib = os.path.join(toolchain_prefix, toolchain_name + "-ranlib" + toolchain_suffix) 88 | 89 | # Generic flags. 90 | self.cflags += [ 91 | "-g", 92 | "-fPIC", 93 | "-DANDROID", 94 | "-ffunction-sections", 95 | "-funwind-tables", 96 | "-fstack-protector-strong", 97 | "-no-canonical-prefixes", 98 | ] 99 | self.cxxflags += [ 100 | "-g", 101 | "-fPIC", 102 | "-DANDROID", 103 | "-ffunction-sections", 104 | "-funwind-tables", 105 | "-fstack-protector-strong", 106 | "-no-canonical-prefixes", 107 | ] 108 | self.ldflags += [ 109 | "-Wl,--build-id", 110 | "-Wl,--warn-shared-textrel", 111 | "-Wl,--fatal-warnings", 112 | ] 113 | # should be only for executables 114 | self.ldflags += [ 115 | "-pie", 116 | "-Wl,--gc-sections", 117 | "-Wl,-z,nocopyreloc", 118 | ] 119 | 120 | if toolchain == "clang": 121 | self.cflags.append("-fno-limit-debug-info") 122 | self.cxxflags.append("-fno-limit-debug-info") 123 | 124 | 125 | # Toolchain and ABI specific flags. 126 | if target == "arm-android-v5te": 127 | mflags = [ 128 | "-march=armv5te", 129 | # Android NDK tunes for XScale, but ARM1136 is more popular 130 | "-mtune=arm1136jf-s", 131 | "-msoft-float", 132 | ] 133 | if toolchain == "clang": 134 | mflags.append("-fno-integrated-as") 135 | elif target == "arm-android-v7a": 136 | mflags = [ 137 | "-march=armv7-a", 138 | "-mfloat-abi=softfp", 139 | "-mfpu=vfpv3-d16", 140 | ] 141 | if toolchain == "clang": 142 | mflags.append("-fno-integrated-as") 143 | self.ldflags.append("-Wl,--fix-cortex-a8") 144 | elif target == "aarch64-android-v8a": 145 | mflags = [] 146 | elif target == "x86-android": 147 | # http://b.android.com/222239 148 | # http://b.android.com/220159 (internal http://b/31809417) 149 | # x86 devices have stack alignment issues. 150 | mflags = ["-mstackrealign"] 151 | elif target == "x86_64-android": 152 | mflags = [] 153 | elif target == "mips-android": 154 | mflags = ["-mips32"] 155 | if toolchain == "clang": 156 | self.ldflags.append("\"-L${TOOLCHAIN_ROOT}/lib/gcc/${TOOLCHAIN_NAME}/4.9.x/32/mips-r1\"".format( 157 | TOOLCHAIN_ROOT=toolchain_root, TOOLCHAIN_NAME=toolchain_name)) 158 | elif target == "mips64-android": 159 | mflags = [] 160 | if toolchain == "clang": 161 | mflags.append("-fintegrated-as") 162 | 163 | self.cflags += mflags 164 | self.cxxflags += mflags 165 | 166 | stl = "c++_static" 167 | # STL specific flags. 168 | assert stl in ["system", "stlport_shared", "stlport_static", "gnustl_shared", "gnustl_static", "c++_shared", "c++_static"] 169 | stl_static_libs, stl_shared_libs = list(), list() 170 | if stl == "system": 171 | stl_prefix = os.path.join("gnu-libstdc++", "4.9") 172 | self.cxxflags += ["-I", os.path.join(self.ndk_root, "sources", "cxx-stl", "system", "include")] 173 | stl_static_libs = ["supc++"] 174 | elif stl.startswith("stlport_"): 175 | stl_prefix = "stlport" 176 | self.cxxflags += [ 177 | "-I", os.path.join(self.ndk_root, "sources", "cxx-stl", stl_prefix, "stlport"), 178 | "-I", os.path.join(self.ndk_root, "sources", "cxx-stl", "gabi++", "include"), 179 | ] 180 | if stl == "stlport_static": 181 | stl_static_libs = ["stlport_static"] 182 | else: 183 | stl_shared_libs = ["stlport_shared"] 184 | elif stl.startswith("gnustl_"): 185 | stl_prefix = os.path.join("gnu-libstdc++", "4.9") 186 | self.cxxflags += [ 187 | "-I", os.path.join(self.ndk_root, "sources", "cxx-stl", stl_prefix, "include"), 188 | "-I", os.path.join(self.ndk_root, "sources", "cxx-stl", stl_prefix, "libs", android_abi, "include"), 189 | "-I", os.path.join(self.ndk_root, "sources", "cxx-stl", stl_prefix, "include", "backward"), 190 | ] 191 | if stl == "gnustl_static": 192 | stl_static_libs = ["gnustl_static"] 193 | else: 194 | stl_static_libs = ["supc++", "gnustl_shared"] 195 | elif stl.startswith("c++_"): 196 | stl_prefix = "llvm-libc++" 197 | if target.is_arm: 198 | self.ldflags.append("-Wl,--exclude-libs,libunwind.a") 199 | if toolchain == "gcc": 200 | self.cxxflags.append("-fno-strict-aliasing") 201 | 202 | # Add the libc++ lib directory to the path so the linker scripts can pick up the extra libraries. 203 | self.ldflags += ["-L", os.path.join(self.ndk_root, "sources", "cxx-stl", stl_prefix, "libs", android_abi)] 204 | self.cxxflags += [ 205 | "-I", os.path.join(self.ndk_root, "sources", "cxx-stl", stl_prefix, "include"), 206 | "-I", os.path.join(self.ndk_root, "sources", "android", "support", "include"), 207 | "-I", os.path.join(self.ndk_root, "sources", "cxx-stl", "llvm-libc++abi", "include"), 208 | ] 209 | if stl == "c++_static": 210 | stl_static_libs = ["c++"] 211 | else: 212 | stl_shared_libs = ["c++"] 213 | 214 | for static_lib in stl_static_libs: 215 | self.ldlibs.append("\"" + os.path.join(self.ndk_root, "sources", "cxx-stl", stl_prefix, "libs", android_abi, "lib" + static_lib + ".a") + "\"") 216 | for shared_lib in stl_shared_libs: 217 | self.ldlibs.append("\"" + os.path.join(self.ndk_root, "sources", "cxx-stl", stl_prefix, "libs", android_abi, "lib" + static_lib + ".so") + "\"") 218 | if target == "arm-android-v5te" and not stl is None and not stl == "system": 219 | self.ldflags.append("-latomic") 220 | 221 | 222 | def write_variables(self, ninja): 223 | super(AndroidToolchain, self).write_variables(ninja) 224 | 225 | ninja.variable("adb", os.path.join(self.sdk_root, "platform-tools", "adb")) 226 | 227 | def write_rules(self, ninja): 228 | super(AndroidToolchain, self).write_rules(ninja, write_library=False, write_run=False) 229 | 230 | ninja.rule("run", "$adb push $in /data/local/tmp/$path && $adb shell /data/local/tmp/$path $args", 231 | description="RUN $path", pool="console") 232 | -------------------------------------------------------------------------------- /confu/tools/toolchains/base.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | 4 | class Toolchain(object): 5 | def __init__(self, target): 6 | super(Toolchain, self).__init__() 7 | self.target = target 8 | 9 | def write_variables(self, ninja): 10 | pass 11 | 12 | def write_rules(self, ninja): 13 | pass 14 | -------------------------------------------------------------------------------- /confu/tools/toolchains/emscripten.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from confu.platform import Platform 4 | from confu.tools.toolchains import UnixToolchain 5 | import confu.globals 6 | import confu.platform 7 | 8 | import os 9 | import logging 10 | 11 | logger = logging.getLogger("confu") 12 | 13 | 14 | class EmscriptenToolchain(UnixToolchain): 15 | def __init__(self, target, toolchain): 16 | super(EmscriptenToolchain, self).__init__(target) 17 | assert isinstance(target, Platform) 18 | assert target in ["asmjs-emscripten", "wasm32-emscripten"] 19 | assert isinstance(toolchain, str) 20 | 21 | # Locate Emscripten SDK 22 | try: 23 | # Locate using configuration file in home directory 24 | emscripten_dotfile = os.path.expanduser("~/.emscripten") 25 | with open(emscripten_dotfile) as emscripten_config: 26 | emscripten_setup = compile(emscripten_config.read(), emscripten_dotfile, 'exec') 27 | emscripten_vars = dict() 28 | exec(emscripten_setup, emscripten_vars) 29 | 30 | for var in emscripten_vars: 31 | if var.isupper(): 32 | logger.debug("Emscripten configuration: {key}={value}".format( 33 | key=var, value=str(emscripten_vars[var]))) 34 | 35 | self.sdk_root = emscripten_vars["EMSCRIPTEN_ROOT"] 36 | self.nodejs = emscripten_vars["NODE_JS"] 37 | except: 38 | # Locate using environment variables 39 | self.sdk_root = os.getenv("EMSCRIPTEN") 40 | if self.sdk_root is None: 41 | raise EnvironmentError("Emscripten SDK not found: " 42 | "set Emscripten environment variable to SDK path") 43 | 44 | self.nodejs = "node" 45 | 46 | # Check toolchain and choose if needed 47 | suffix = ".bat" if confu.platform.host.is_windows else "" 48 | self.cc = os.path.join(self.sdk_root, "emcc" + suffix) 49 | self.cxx = os.path.join(self.sdk_root, "em++" + suffix) 50 | self.ar = os.path.join(self.sdk_root, "emar" + suffix) 51 | self.ranlib = os.path.join(self.sdk_root, "emranlib" + suffix) 52 | 53 | def write_variables(self, ninja): 54 | super(EmscriptenToolchain, self).write_variables(ninja) 55 | 56 | ninja.variable("nodejs", self.nodejs) 57 | 58 | def write_rules(self, ninja): 59 | super(EmscriptenToolchain, self).write_rules(ninja, write_library=False, write_run=False) 60 | 61 | ninja.rule("run", "$nodejs $in $args", 62 | description="RUN $path", pool="console") 63 | -------------------------------------------------------------------------------- /confu/tools/toolchains/nacl.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from confu.platform import Platform 4 | from confu.tools.toolchains import UnixToolchain 5 | import confu.globals 6 | import confu.platform 7 | import os 8 | 9 | 10 | class NaClToolchain(UnixToolchain): 11 | def __init__(self, target, toolchain): 12 | super(NaClToolchain, self).__init__(target) 13 | assert isinstance(target, Platform) 14 | assert target in ["x86_64-nacl-gnu", "x86_64-nacl-newlib", "pnacl-nacl-newlib"] 15 | assert isinstance(toolchain, str) 16 | 17 | # Check if we are running in a supported environment 18 | if confu.platform.host not in ["x86_64-linux-gnu", "x86_64-macos", "x86_64-windows"]: 19 | raise EnvironmentError("Cross-compilation for {target} is not supported on {host}: " 20 | "x86-64 Linux, Mac, or Windows system required" 21 | .format(target=target, host=confu.platform.host)) 22 | 23 | # Locate Native Client SDK 24 | self.sdk_root = os.getenv("NACL_SDK_ROOT") 25 | if self.sdk_root is None: 26 | raise EnvironmentError("Native Client SDK not found: " 27 | "set NACL_SDK_ROOT environment variable to SDK path") 28 | 29 | # Check toolchain and choose if needed 30 | if target == "x86_64-nacl-gnu": 31 | if toolchain not in ["auto", "gnu"]: 32 | raise EnvironmentError("Cross-compilation for {target} requires a GNU toolchain".format(target=target)) 33 | 34 | toolchain = "gnu" 35 | elif target in ["x86_64-nacl-newlib", "pnacl-nacl-newlib"]: 36 | if toolchain not in ["auto", "clang"]: 37 | raise EnvironmentError("Cross-compilation for {target} requires a Clang toolchain".format(target=target)) 38 | 39 | if toolchain == "auto": 40 | toolchain = "clang" 41 | assert toolchain in ["gnu", "clang"] 42 | 43 | toolchain_library_subdir_map = { 44 | ("linux", "x86_64-nacl-gnu"): ("linux_x86_glibc", "glibc_x86_64"), 45 | ("linux", "x86_64-nacl-newlib"): ("linux_pnacl", "clang-newlib_x86_64"), 46 | ("linux", "pnacl-nacl-newlib"): ("linux_pnacl", "pnacl"), 47 | ("macos", "x86_64-nacl-gnu"): ("mac_x86_glibc", "glibc_x86_64"), 48 | ("macos", "x86_64-nacl-newlib"): ("mac_pnacl", "clang-newlib_x86_64"), 49 | ("macos", "pnacl-nacl-newlib"): ("mac_pnacl", "pnacl"), 50 | ("windows", "x86_64-nacl-gnu"): ("win_x86_glibc", "glibc_x86_64"), 51 | ("windows", "x86_64-nacl-newlib"): ("win_pnacl", "clang-newlib_x86_64"), 52 | ("windows", "pnacl-nacl-newlib"): ("win_pnacl", "pnacl"), 53 | } 54 | 55 | toolchain_subdir, library_subdir = toolchain_library_subdir_map[confu.platform.host.os, target.name] 56 | self.toolchain_dir = os.path.join(self.sdk_root, "toolchain", toolchain_subdir) 57 | self.pepper_include_dir = os.path.join(self.sdk_root, "include") 58 | self.pepper_lib_dir = os.path.join(self.sdk_root, "toolchain", "lib", library_subdir, "Release") 59 | 60 | toolchain_compiler_map = { 61 | "x86_64-nacl-gnu": ("x86_64-nacl-gcc", "x86_64-nacl-g++", "x86_64-nacl-"), 62 | "x86_64-nacl-newlib": ("x86_64-nacl-clang", "x86_64-nacl-clang++", "x86_64-nacl-"), 63 | "pnacl-nacl-newlib": ("pnacl-clang", "pnacl-clang++", "pnacl-"), 64 | } 65 | cc, cxx, prefix = toolchain_compiler_map[target.name] 66 | suffix = ".bat" if confu.platform.host.is_windows else "" 67 | self.cc = os.path.join(self.toolchain_dir, "bin", cc + suffix) 68 | self.cxx = os.path.join(self.toolchain_dir, "bin", cxx + suffix) 69 | self.ranlib = os.path.join(self.toolchain_dir, "bin", prefix + "ranlib" + suffix) 70 | self.ar = os.path.join(self.toolchain_dir, "bin", prefix + "ar" + suffix) 71 | self.strip = os.path.join(self.toolchain_dir, "bin", prefix + "strip" + suffix) 72 | 73 | if target.is_pnacl: 74 | self.finalize = os.path.join(self.toolchain_dir, "bin", "pnacl-finalize") 75 | self.compress = os.path.join(self.toolchain_dir, "bin", "pnacl-compress") 76 | self.translate = os.path.join(self.toolchain_dir, "bin", "pnacl-translate") 77 | else: 78 | self.objcopy = os.path.join(self.toolchain_dir, "bin", prefix + "objcopy") 79 | 80 | self.sel_ldr = os.path.join(self.sdk_root, "tools", "sel_ldr.py") 81 | 82 | def write_variables(self, ninja): 83 | super(NaClToolchain, self).write_variables(ninja) 84 | 85 | ninja.variable("includes", "-isystem " + self.pepper_include_dir) 86 | ninja.variable("lddirs", "-L" + self.pepper_lib_dir) 87 | 88 | if self.target.is_pnacl: 89 | ninja.variable("finalize", self.finalize) 90 | ninja.variable("compress", self.compress) 91 | ninja.variable("translate", self.translate) 92 | 93 | ninja.variable("sel_ldr", self.sel_ldr) 94 | 95 | def write_rules(self, ninja): 96 | super(NaClToolchain, self).write_rules(ninja, 97 | write_library=self.target.is_newlib, 98 | write_run=False) 99 | 100 | if self.target.is_pnacl: 101 | ninja.rule("finalize", "$finalize --compress -o $out $in", 102 | description="FINALIZE $path") 103 | ninja.rule("translate", "$translate --allow-llvm-bitcode-input -O3 -threads=auto -arch x86-64 $in -o $out", 104 | description="TRANSLATE $path") 105 | 106 | ninja.rule("run", "$sel_ldr -p -- $in $args", 107 | description="RUN $path", pool="console") 108 | -------------------------------------------------------------------------------- /confu/tools/toolchains/unix.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from confu.tools.toolchains import Toolchain 4 | 5 | 6 | class UnixToolchain(Toolchain): 7 | def __init__(self, target): 8 | super(UnixToolchain, self).__init__(target) 9 | self.cc = None 10 | self.cxx = None 11 | self.ar = None 12 | self.ranlib = None 13 | self.strip = None 14 | self.objcopy = None 15 | 16 | self.cflags = ["-std=gnu99" if self.target.is_nacl or self.target.is_pnacl else "-std=gnu11", "-g"] 17 | self.cxxflags = ["-std=gnu++0x" if self.target == "x86_64-nacl-gnu" else "-std=gnu++11", "-g"] 18 | self.ldflags = [] 19 | self.ldlibs = [] 20 | self.optflag = "-O2" 21 | if self.target.is_glibc or self.target.is_freebsd: 22 | self.cflags.append("-pthread") 23 | self.cxxflags.append("-pthread") 24 | self.ldflags.append("-pthread") 25 | 26 | def write_variables(self, ninja): 27 | if self.cc is not None: 28 | ninja.variable("cc", self.cc) 29 | if self.cxx is not None: 30 | ninja.variable("cxx", self.cxx) 31 | if self.ar is not None: 32 | ninja.variable("ar", self.ar) 33 | if self.ranlib is not None: 34 | ninja.variable("ranlib", self.ranlib) 35 | if self.strip is not None: 36 | ninja.variable("strip", self.strip) 37 | if self.objcopy is not None: 38 | ninja.variable("objcopy", self.objcopy) 39 | 40 | ninja.variable("cflags", " ".join(self.cflags)) 41 | ninja.variable("cxxflags", " ".join(self.cxxflags)) 42 | ninja.variable("ldflags", " ".join(self.ldflags)) 43 | ninja.variable("ldlibs", " ".join(self.ldlibs)) 44 | ninja.variable("optflags", self.optflag) 45 | 46 | def write_rules(self, ninja, write_library=True, write_run=True): 47 | ninja.rule("cc", "$cc -o $out -c $in -MMD -MF $out.d $cflags $optflags $macro $includes", 48 | deps="gcc", depfile="$out.d", 49 | description="CC $path") 50 | 51 | ninja.rule("cxx", "$cxx -o $out -c $in -MMD -MF $out.d $cxxflags $optflags $macro $includes", 52 | deps="gcc", depfile="$out.d", 53 | description="CXX $path") 54 | 55 | emscripten_linker_flags = "" 56 | if self.target.is_emscripten: 57 | if self.target.is_wasm: 58 | emscripten_linker_flags = "$optflags -s WASM=1 $emflags " 59 | else: 60 | emscripten_linker_flags = "$optflags $emflags " 61 | ninja.rule("executable", "$linker {emscripten_flags}$ldflags $lddirs -o $out $in $ldlibs" 62 | .format(emscripten_flags=emscripten_linker_flags), 63 | description="LINK $path") 64 | 65 | if write_library: 66 | ninja.rule("library", "$linker {emscripten_flags}{library_flag} $ldflags $lddirs -o $out $in $ldlibs" 67 | .format(library_flag="-dynamiclib" if self.target.is_macos else "-shared", 68 | emscripten_flags=emscripten_linker_flags), 69 | description="LINK $path") 70 | 71 | ninja.rule("archive", "$ar rcs $out $in", 72 | description="AR $path") 73 | 74 | if write_run: 75 | ninja.rule("run", "$in $args", 76 | description="RUN $path", pool="console") 77 | -------------------------------------------------------------------------------- /confu/utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | 4 | logger = logging.getLogger("confu") 5 | 6 | 7 | def get_root_dir(level=2): 8 | import sys 9 | frame = sys._getframe(level) 10 | code = frame.f_code 11 | filename = code.co_filename 12 | 13 | from os import path 14 | return path.dirname(path.abspath(path.normpath(filename))) 15 | 16 | 17 | def format_macro(name, value): 18 | if value is None: 19 | return "-D" + name 20 | else: 21 | return "-D" + name + "=" + str(value) 22 | 23 | 24 | def qualified_type(var): 25 | if var is None: 26 | return "None" 27 | else: 28 | module = var.__class__.__module__ 29 | type = var.__class__.__name__ 30 | if module is None or module == "__builtin__": 31 | return type 32 | else: 33 | return module + "." + type 34 | -------------------------------------------------------------------------------- /confu/validators.py: -------------------------------------------------------------------------------- 1 | import os 2 | import six 3 | 4 | 5 | def validate_include_dir(include_dir, root_dir): 6 | include_dir = os.path.normpath(include_dir) 7 | if not os.path.isabs(include_dir): 8 | include_dir = os.path.join(root_dir, include_dir) 9 | if not os.path.isdir(include_dir): 10 | raise ValueError("Include directory {include_dir} does not exist".format(include_dir=include_dir)) 11 | 12 | return include_dir 13 | 14 | 15 | def validate_include_dirs(include_dirs, root_dir): 16 | import collections 17 | if isinstance(include_dirs, str): 18 | return [validate_include_dir(include_dirs, root_dir)] 19 | elif isinstance(include_dirs, collections.Mapping): 20 | return sum((validate_include_dirs(include_dirs_collection, root_dir) for include_dirs_collection, platform_match 21 | in six.iteritems(include_dirs) if platform_match), list()) 22 | elif isinstance(include_dirs, collections.Iterable): 23 | return [validate_include_dir(include_dir, root_dir) for include_dir in include_dirs] 24 | else: 25 | raise TypeError("Invalid type of include directories: string, mapping, or iterable expected") 26 | 27 | 28 | def validate_source_dir(source_dir, root_dir): 29 | if not isinstance(source_dir, str): 30 | raise TypeError("Invalid type of source directory: string expected") 31 | 32 | source_dir = os.path.normpath(source_dir) 33 | if os.path.isabs(source_dir): 34 | rel_source_dir = os.path.relpath(source_dir, root_dir) 35 | if rel_source_dir.startswith(".."): 36 | raise ValueError("Source directory {source_dir} is outside of root directory {root_dir}" 37 | .format(source_dir=source_dir, root_dir=root_dir)) 38 | else: 39 | if source_dir.startswith(".."): 40 | raise ValueError("Relative source directory {source_dir} points outside of root directory" 41 | .format(source_dir=source_dir)) 42 | source_dir = os.path.join(root_dir, source_dir) 43 | if not os.path.isdir(source_dir): 44 | raise ValueError("Source directory {source_dir} does not exist".format(source_dir=source_dir)) 45 | 46 | return source_dir 47 | 48 | 49 | def validate_source_path(source_path, source_dir): 50 | if not isinstance(source_path, str): 51 | raise TypeError("Invalid type of source path: string expected") 52 | 53 | source_path = os.path.normpath(source_path) 54 | if os.path.isabs(source_path): 55 | rel_source_path = os.path.relpath(source_path, source_dir) 56 | if rel_source_path.startswith(".."): 57 | raise ValueError("Source path {source_path} is outside of source directory {source_dir}" 58 | .format(source_path=source_path, source_dir=source_dir)) 59 | else: 60 | if source_path.startswith(".."): 61 | raise ValueError("Relative source path {source_path} points outside of source directory" 62 | .format(source_path=source_path)) 63 | source_path = os.path.join(source_dir, source_path) 64 | 65 | if not os.path.isfile(source_path): 66 | raise ValueError("Specified source file {source_path} does not exist" 67 | .format(source_path=source_path)) 68 | 69 | return source_path 70 | 71 | 72 | def validate_source_paths(source_paths, source_dir): 73 | import collections 74 | if isinstance(source_paths, str): 75 | return [validate_source_path(source_paths, source_dir)] 76 | elif isinstance(source_paths, collections.Mapping): 77 | return sum((validate_source_paths(source_paths_collection, source_dir) 78 | for source_paths_collection, platform_match in six.iteritems(source_paths) if platform_match), list()) 79 | elif isinstance(source_paths, collections.Iterable): 80 | return [validate_source_path(source_path, source_dir) for source_path in source_paths] 81 | else: 82 | raise TypeError("Invalid type of source paths: string, mapping, or iterable expected") 83 | 84 | 85 | def validate_export_function(name): 86 | if not isinstance(name, str): 87 | raise TypeError("Invalid type of function name: string expected") 88 | 89 | import re 90 | if not re.match("^\w+$", name): 91 | raise ValueError("Invalid function name {name}: a valid C identifier expected".format(name=name)) 92 | 93 | return name 94 | 95 | 96 | def validate_export_functions(functions): 97 | import collections 98 | if isinstance(functions, str): 99 | return [validate_export_function(functions)] 100 | elif isinstance(functions, collections.Mapping): 101 | return sum((validate_export_functions(functions_collection) for functions_collection, platform_match 102 | in six.iteritems(functions) if platform_match), list()) 103 | elif isinstance(functions, collections.Iterable): 104 | return [validate_export_function(function) for function in functions] 105 | else: 106 | raise TypeError("Invalid type of export functions: string, mapping, or iterable expected") 107 | 108 | 109 | def validate_package_name(name): 110 | if not isinstance(name, str): 111 | raise TypeError("Invalid type of package name: string expected") 112 | 113 | import re 114 | if not re.match("^[_a-zA-Z]\w*$", name): 115 | raise ValueError("Invalid package name {name}: a valid Python identifier expected".format(name=name)) 116 | 117 | if name.startswith("_"): 118 | raise ValueError("Invalid package name {name}: names starting with an underscore are reserved" 119 | .format(name=name)) 120 | 121 | return name.lower() 122 | 123 | 124 | def validate_emscripten_memory_size(memory_size): 125 | if isinstance(memory_size, six.integer_types): 126 | if memory_size <= 0: 127 | raise ValueError("Invalid memory size value {size}: a positive number expected".format(size=memory_size)) 128 | 129 | return ["-s", "TOTAL_MEMORY=" + str(memory_size)] 130 | elif isinstance(memory_size, str): 131 | import re 132 | match = re.match(r"^(\d+)([MK]?)$", memory_size) 133 | if not match: 134 | raise ValueError("Invalid memory size value {size}: " 135 | "an integer expected with an optional M(ega) or K(ilo) suffix " 136 | "(e.g. 256M)".format(size=memory_size)) 137 | 138 | number = int(match.group(1)) 139 | suffix = match.group(2) 140 | memory_size = {"": number, "K": number * 1024, "M": number * 1048576}[suffix] 141 | return validate_emscripten_memory_size(memory_size) 142 | elif memory_size is all: 143 | return ["-s", "ALLOW_MEMORY_GROWTH=1"] 144 | else: 145 | raise TypeError("Invalid memory size type: an integer, string, or all expected") 146 | 147 | 148 | def validate_module_name(name): 149 | if not isinstance(name, str): 150 | raise TypeError("Invalid type of module name: string expected") 151 | 152 | import re 153 | if not re.match("^[_a-zA-Z]\w*$", name): 154 | raise ValueError("Invalid module name {name}: a valid Python identifier expected".format(name=name)) 155 | 156 | if name.startswith("_"): 157 | raise ValueError("Invalid module name {name}: names starting with an underscore are reserved" 158 | .format(name=name)) 159 | 160 | return name.lower() 161 | 162 | 163 | platform_alternatives = { 164 | "x86_64-linux-gnu": ["x86_64-linux"], 165 | "x86_64-linux-gnux32": ["x32", "linux-x32"], 166 | "ppc64le-linux-gnu": ["ppc64le-linux"], 167 | "arm-linux-gnueabihf": ["arm-linux", "linux-gnueabihf"], 168 | "aarch64-linux-gnu": ["arm64-linux", "aarch64-linux"], 169 | "arm-android-v7a": ["arm-android"], 170 | "aarch64-android-v8a": ["arm64-android", "arm64-android-v8a", "aarch64-android"], 171 | "x86_64-android": [], 172 | "x86-android": [], 173 | "x86_64-macos": [], 174 | "x86_64-freebsd": [], 175 | "x86_64-nacl-gnu": [], 176 | "x86_64-nacl-newlib": ["x86_64-nacl"], 177 | "pnacl-nacl-newlib": ["pnacl", "pnacl-nacl", "pnacl-newlib"], 178 | "asmjs-emscripten": ["asmjs"], 179 | "wasm32-emscripten": ["wasm-emscripten", "wasm"], 180 | } 181 | platform_mappings = {name: name for name in platform_alternatives} 182 | for name, alternatives in six.iteritems(platform_alternatives): 183 | assert not any(alternative in platform_mappings for alternative in alternatives) 184 | platform_mappings.update({alternative: name for alternative in alternatives}) 185 | 186 | def validate_platform_name(name): 187 | if not isinstance(name, str): 188 | raise TypeError("Invalid type of platform name: string expected") 189 | 190 | if name in platform_mappings: 191 | return platform_mappings[name] 192 | else: 193 | raise ValueError("Invalid platform name {name}".format(name=name)) 194 | 195 | 196 | def validate_dependency(dependency, build): 197 | from confu.builds import Build, Module, ModuleCollection 198 | if isinstance(dependency, Build): 199 | return dependency.modules.default 200 | elif isinstance(dependency, ModuleCollection): 201 | return dependency.default 202 | elif isinstance(dependency, Module): 203 | return dependency 204 | elif isinstance(dependency, str): 205 | import re 206 | if not re.match("^[\w*-\.]+$", name): 207 | raise ValueError("Invalid system dependency {name}: name of a system library expected".format(name=name)) 208 | 209 | return dependency 210 | else: 211 | raise TypeError("Invalid dependency type: Build, Module, or name of a system library expected") 212 | 213 | 214 | def validate_dependencies(dependencies, build): 215 | import collections 216 | from confu.builds import Build, Module 217 | if isinstance(dependencies, (str, Build, Module)): 218 | return [validate_dependency(dependencies, build)] 219 | elif isinstance(dependencies, collections.Mapping): 220 | return sum((validate_dependencies(dependencies_collection, build) for dependencies_collection, platform_match 221 | in six.iteritems(dependencies) if platform_match), list()) 222 | elif isinstance(dependencies, collections.Iterable): 223 | return [validate_dependency(dependency, build) for dependency in dependencies] 224 | else: 225 | raise TypeError("Invalid type of dependencies: Build, Module, mapping, or iterable expected") 226 | 227 | 228 | -------------------------------------------------------------------------------- /confu/x86/__init__.py: -------------------------------------------------------------------------------- 1 | from confu.x86.isa import sse2, sse3, ssse3, sse4_1, sse4_2, avx, f16c, fma3, fma4, xop, avx2 2 | from confu.x86.isa import aes, pclmulqdq, sha 3 | from confu.x86.isa import lzcnt, popcnt, tbm, bmi, bmi2 4 | -------------------------------------------------------------------------------- /confu/x86/isa.py: -------------------------------------------------------------------------------- 1 | from confu.isa import InstructionSet 2 | 3 | _isa_subsets = { 4 | "avx2": ["sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "avx", "fma3", "f16c"], 5 | "fma4": ["sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "avx"], 6 | "fma3": ["sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "avx", "f16c"], 7 | "xop": ["sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "avx"], 8 | "f16c": ["sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "avx"], 9 | "avx": ["sse2", "sse3", "ssse3", "sse4.1", "sse4.2"], 10 | "sse4.2": ["sse2", "sse3", "ssse3", "sse4.1"], 11 | "sse4.1": ["sse2", "sse3", "ssse3", "sse2"], 12 | "ssse3": ["sse2", "sse3"], 13 | "sse3": ["sse2"], 14 | } 15 | 16 | def _remove_isa_subset(tags, isa): 17 | if isa in _isa_subsets: 18 | for isa_subset in _isa_subsets[isa]: 19 | if isa_subset in tags: 20 | tags.remove(isa_subset) 21 | 22 | def _generate_simd_flags(tags, compiler): 23 | flags = [] 24 | if "avx2" in tags: 25 | flags.append("-mf16c", "-mfma", "-mavx2") 26 | _remove_isa_subset(tags, "avx2") 27 | if "fma4" in tags: 28 | flags += ["-mavx", "-mfma4"] 29 | _remove_isa_subset(tags, "fma4") 30 | if "fma3" in tags: 31 | flags += ["-mavx", "-mf16c", "-mfma"] 32 | _remove_isa_subset(tags, "fma3") 33 | if "xop" in tags: 34 | flags += ["-mavx", "-mxop"] 35 | _remove_isa_subset(tags, "xop") 36 | if "f16c" in tags: 37 | flags += ["-mavx", "-mf16c"] 38 | _remove_isa_subset(tags, "f16c") 39 | if "avx" in tags: 40 | flags.append("-mavx") 41 | _remove_isa_subset(tags, "avx") 42 | if "sse4.2" in tags: 43 | flags.append("-msse4.2") 44 | _remove_isa_subset(tags, "sse4.2") 45 | if "sse4.1" in tags: 46 | flags.append("-msse4.1") 47 | _remove_isa_subset(tags, "sse4.1") 48 | if "ssse3" in tags: 49 | flags.append("-mssse3") 50 | _remove_isa_subset(tags, "ssse3") 51 | if "sse3" in tags: 52 | flags.append("-msse3") 53 | _remove_isa_subset(tags, "sse3") 54 | if "sse2" in tags: 55 | flags.append("-msse2") 56 | _remove_isa_subset(tags, "sse2") 57 | return flags 58 | 59 | def _generate_crypto_flags(tags, compiler): 60 | flags = [] 61 | if "aes" in tags: 62 | flags.append("-maes") 63 | if "pclmulqdq" in tags: 64 | flags.append("-mpclmul") 65 | if "sha" in tags: 66 | flags.append("-msha") 67 | return flags 68 | 69 | def _generate_scalar_flags(tags, compiler): 70 | flags = [] 71 | if "bmi2" in tags: 72 | flags.append("-mbmi2") 73 | elif "bmi" in tags: 74 | flags.append("-mbmi") 75 | if "tbm" in tags: 76 | flags.append("-mtbm") 77 | if "popcnt" in tags: 78 | flags.append("-mpopcnt") 79 | if "lzcnt" in tags: 80 | flags.append("-mlzcnt") 81 | return flags 82 | 83 | sse2 = InstructionSet("sse2", generate_flags_fn=_generate_simd_flags) 84 | sse3 = InstructionSet("sse3", generate_flags_fn=_generate_simd_flags) 85 | ssse3 = InstructionSet("ssse3", generate_flags_fn=_generate_simd_flags) 86 | sse4_1 = InstructionSet("sse4.1", generate_flags_fn=_generate_simd_flags) 87 | sse4_2 = InstructionSet("sse4.2", generate_flags_fn=_generate_simd_flags) 88 | avx = InstructionSet("avx", generate_flags_fn=_generate_simd_flags) 89 | f16c = InstructionSet("f16c", generate_flags_fn=_generate_simd_flags) 90 | fma3 = InstructionSet("fma3", generate_flags_fn=_generate_simd_flags) 91 | fma4 = InstructionSet("fma4", generate_flags_fn=_generate_simd_flags) 92 | xop = InstructionSet("xop", generate_flags_fn=_generate_simd_flags) 93 | avx2 = InstructionSet("avx2", generate_flags_fn=_generate_simd_flags) 94 | 95 | aes = InstructionSet("aes", generate_flags_fn=_generate_crypto_flags) 96 | pclmulqdq = InstructionSet("pclmulqdq", generate_flags_fn=_generate_crypto_flags) 97 | sha = InstructionSet("sha", generate_flags_fn=_generate_crypto_flags) 98 | 99 | lzcnt = InstructionSet("lzcnt", generate_flags_fn=_generate_scalar_flags) 100 | popcnt = InstructionSet("popcnt", generate_flags_fn=_generate_scalar_flags) 101 | tbm = InstructionSet("tbm", generate_flags_fn=_generate_scalar_flags) 102 | bmi = InstructionSet("bmi", generate_flags_fn=_generate_scalar_flags) 103 | bmi2 = InstructionSet("bmi2", generate_flags_fn=_generate_scalar_flags) 104 | -------------------------------------------------------------------------------- /install_pygit2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | DOWNLOAD_DIR=/tmp/confu-deps 6 | 7 | NINJA_DIR="$DOWNLOAD_DIR/ninja" 8 | NINJA_URL="https://github.com/ninja-build/ninja/archive/v1.7.2.tar.gz" 9 | 10 | CMAKE_DIR="$DOWNLOAD_DIR/cmake" 11 | CMAKE_URL="https://cmake.org/files/v3.7/cmake-3.7.2.tar.gz" 12 | 13 | LIBSSL_DIR="$DOWNLOAD_DIR/libssl" 14 | LIBSSL_URL="https://www.openssl.org/source/openssl-1.0.2k.tar.gz" 15 | 16 | LIBGIT2_DIR="$DOWNLOAD_DIR/libgit2" 17 | LIBGIT2_URL="https://github.com/libgit2/libgit2/archive/v0.25.1.tar.gz" 18 | LIBGIT2_LIB=libgit2.so.0.25.1 19 | LIBGIT2_SONAME=libgit2.so.25 20 | 21 | function install_ninja() { 22 | mkdir -p "$DOWNLOAD_DIR" 23 | if [ -f "$DOWNLOAD_DIR/ninja.tgz" ] 24 | then 25 | rm -f "$DOWNLOAD_DIR/ninja.tgz" 26 | fi 27 | wget --no-check-certificate "$NINJA_URL" -O "$DOWNLOAD_DIR/ninja.tgz" 28 | if [ -d "$NINJA_DIR" ] 29 | then 30 | rm -rf "$NINJA_DIR" 31 | fi 32 | mkdir -p "$NINJA_DIR" 33 | tar xf "$DOWNLOAD_DIR/ninja.tgz" -C "$NINJA_DIR" --strip-components=1 34 | pushd "$NINJA_DIR" 35 | python configure.py --bootstrap 36 | mkdir -p "$HOME/.local/bin" 37 | cp -f ninja "$HOME/.local/bin/ninja" 38 | popd 39 | } 40 | 41 | function build_cmake() { 42 | mkdir -p "$DOWNLOAD_DIR" 43 | if [ -f "$DOWNLOAD_DIR/cmake.tgz" ] 44 | then 45 | rm -f "$DOWNLOAD_DIR/cmake.tgz" 46 | fi 47 | wget --no-check-certificate "$CMAKE_URL" -O "$DOWNLOAD_DIR/cmake.tgz" 48 | if [ -d "$CMAKE_DIR" ] 49 | then 50 | rm -rf "$CMAKE_DIR" 51 | fi 52 | mkdir -p "$CMAKE_DIR" 53 | tar xf "$DOWNLOAD_DIR/cmake.tgz" -C "$CMAKE_DIR" --strip-components=1 54 | pushd "$CMAKE_DIR" 55 | ./configure --prefix="$CMAKE_DIR/install" --parallel=$(nproc) 56 | make -j $(nproc) 57 | make install 58 | popd 59 | } 60 | 61 | function build_libssl() { 62 | mkdir -p "$DOWNLOAD_DIR" 63 | if [ -f "$DOWNLOAD_DIR/libssl.tgz" ] 64 | then 65 | rm -f "$DOWNLOAD_DIR/libssl.tgz" 66 | fi 67 | wget --no-check-certificate "$LIBSSL_URL" -O "$DOWNLOAD_DIR/libssl.tgz" 68 | if [ -d "$LIBSSL_DIR" ] 69 | then 70 | rm -rf "$LIBSSL_DIR" 71 | fi 72 | mkdir -p "$LIBSSL_DIR" 73 | tar xf "$DOWNLOAD_DIR/libssl.tgz" -C "$LIBSSL_DIR" --strip-components=1 74 | pushd "$LIBSSL_DIR" 75 | ./config "--prefix=$LIBSSL_DIR/install" "--openssldir=$LIBSSL_DIR/install" no-shared no-asm -fPIC 76 | # Do not use -j option: libssl Makefile doesn't support parallel builds 77 | make 78 | make install 79 | popd 80 | } 81 | 82 | function build_libgit2() { 83 | mkdir -p "$DOWNLOAD_DIR" 84 | if [ -f "$DOWNLOAD_DIR/libgit2.tgz" ] 85 | then 86 | rm -f "$DOWNLOAD_DIR/libgit2.tgz" 87 | fi 88 | wget --no-check-certificate "$LIBGIT2_URL" -O "$DOWNLOAD_DIR/libgit2.tgz" 89 | if [ -d "$LIBGIT2_DIR" ] 90 | then 91 | rm -rf "$LIBGIT2_DIR" 92 | fi 93 | mkdir -p "$LIBGIT2_DIR" 94 | tar xf "$DOWNLOAD_DIR/libgit2.tgz" -C "$LIBGIT2_DIR" --strip-components=1 95 | mkdir -p "$LIBGIT2_DIR/build" 96 | pushd "$LIBGIT2_DIR/build" 97 | "$CMAKE_DIR/install/bin/cmake" -G Ninja \ 98 | "-DCMAKE_LIBRARY_PATH=$LIBSSL_DIR/install/include:$LIBSSL_DIR/install/lib" \ 99 | "-DCMAKE_INSTALL_PREFIX=$LIBGIT2_DIR/install" "-DCMAKE_MAKE_PROGRAM=$NINJA_DIR/ninja" \ 100 | -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DBUILD_CLAR=OFF \ 101 | "-DOPENSSL_ROOT_DIR=$LIBSSL_DIR/install" \ 102 | "-DOPENSSL_SSL_LIBRARY=$LIBSSL_DIR/install/lib/libssl.a" \ 103 | "-DOPENSSL_CRYPTO_LIBRARY=$LIBSSL_DIR/install/lib/libcrypto.a" .. 104 | "$HOME/.local/bin/ninja" 105 | "$HOME/.local/bin/ninja" install 106 | mkdir -p "$HOME/.local/lib" 107 | cp -f "$LIBGIT2_DIR/install/lib/$LIBGIT2_LIB" "$HOME/.local/lib/" 108 | ln -f -s "$HOME/.local/lib/$LIBGIT2_LIB" "$HOME/.local/lib/$LIBGIT2_SONAME" 109 | popd 110 | } 111 | 112 | function install_pygit2() { 113 | LIBGIT2="$LIBGIT2_DIR/install" LDFLAGS="-Wl,-rpath,$HOME/.local/lib,--enable-new-dtags" pip install --user --upgrade --ignore-installed --force-reinstall pygit2==0.25.0 114 | python -c "import pygit2" 115 | } 116 | 117 | function clean() { 118 | rm -rf "$DOWNLOAD_DIR" 119 | } 120 | 121 | function uninstall() { 122 | if [ "$(uname -s)" != "Darwin" ] 123 | then 124 | rm -f "$HOME/.local/bin/ninja" 125 | rm -f "$HOME/.local/lib/$LIBGIT2_LIB" 126 | unlink "$HOME/.local/lib/$LIBGIT2_SONAME" 127 | fi 128 | } 129 | 130 | if [ "$1" == "--uninstall" ] 131 | then 132 | uninstall 133 | else 134 | if [ "$(uname -s)" == "Darwin" ] 135 | then 136 | brew install libgit2 ninja 137 | pip install --user pygit2 138 | else 139 | install_ninja 140 | build_cmake 141 | build_libssl 142 | build_libgit2 143 | install_pygit2 144 | clean 145 | fi 146 | fi 147 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ninja-syntax>=1.7.2 2 | pygit2 3 | six 4 | 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from confu import __version__, __maintainer__, __email__ 4 | from setuptools import setup, find_packages 5 | 6 | 7 | setup( 8 | name="confu", 9 | version=__version__, 10 | description="Configuration generator for Ninja build system", 11 | url="https://github.com/Maratyszcza/confu", 12 | packages=find_packages(), 13 | scripts=["bin/confu"], 14 | keywords=["build", "ninja"], 15 | maintainer=__maintainer__, 16 | maintainer_email=__email__, 17 | package_data={"confu.recipes": ["*.yaml"]}, 18 | requires=[], 19 | classifiers=[ 20 | "Development Status :: 1 - Planning", 21 | "Intended Audience :: Developers", 22 | "License :: OSI Approved :: MIT License", 23 | "Operating System :: OS Independent", 24 | "Programming Language :: Python", 25 | "Programming Language :: Python :: 2", 26 | "Programming Language :: Python :: 3", 27 | "Topic :: Software Development", 28 | "Topic :: Software Development :: Build Tools" 29 | ], 30 | setup_requires=["six"], 31 | install_requires=["six", "PyYAML", "ninja_syntax>=1.7.2"]) 32 | -------------------------------------------------------------------------------- /sphinx/conf.py: -------------------------------------------------------------------------------- 1 | import sphinx_rtd_theme 2 | 3 | from confu import __version__ 4 | 5 | extensions = [ 6 | 'sphinx.ext.autodoc', 7 | 'sphinx.ext.viewcode', 8 | ] 9 | 10 | source_suffix = '.rst' 11 | master_doc = 'index' 12 | 13 | project = u'Confu: Ninja-based configuration system' 14 | copyright = u'2017, Georgia Institute of Technology' 15 | 16 | version = __version__ 17 | release = __version__ 18 | 19 | pygments_style = 'sphinx' 20 | autoclass_content = 'both' 21 | 22 | html_theme = "sphinx_rtd_theme" 23 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 24 | -------------------------------------------------------------------------------- /sphinx/confu.rst: -------------------------------------------------------------------------------- 1 | confu package 2 | ============= 3 | 4 | confu module 5 | --------------------- 6 | 7 | .. automodule:: confu 8 | :members: standard_parser, Build, Platform 9 | -------------------------------------------------------------------------------- /sphinx/index.rst: -------------------------------------------------------------------------------- 1 | Confu: Ninja-based configuration system 2 | ======================================= 3 | 4 | Contents: 5 | 6 | .. toctree:: 7 | :maxdepth: 4 8 | 9 | confu 10 | 11 | 12 | Indices and tables 13 | ================== 14 | 15 | * :ref:`genindex` 16 | * :ref:`modindex` 17 | * :ref:`search` 18 | -------------------------------------------------------------------------------- /sphinx/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | sphinx_rtd_theme 3 | --------------------------------------------------------------------------------