Skip to content

Commit

Permalink
First public version
Browse files Browse the repository at this point in the history
  • Loading branch information
Maratyszcza committed Feb 14, 2017
0 parents commit 029d7a4
Show file tree
Hide file tree
Showing 43 changed files with 2,206 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Ninja files
.ninja_deps
.ninja_log
build.ninja

# PyCharm project files
.idea/

# Build objects and artifacts
confu.egg-info/
dist/
docs/
*.pyc
*.pyo

# System files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2017 Georgia Institute of Technology

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include confu/recipes/*.yaml
6 changes: 6 additions & 0 deletions bin/confu
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env python

from confu.__main__ import main

if __name__ == '__main__':
main()
58 changes: 58 additions & 0 deletions confu/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
__copyright__ = "Copyright 2017, Georgia Institute of Technology"
__license__ = "MIT"
__version_info__ = ('0', '0', '1')
__version__ = '.'.join(__version_info__)
__maintainer__ = "Marat Dukhan"
__email__ = "[email protected]"

import logging
logger = logging.getLogger("confu")
logger.setLevel(logging.INFO)

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)


class ConsoleFormatter(logging.Formatter):
def __init__(self):
super(ConsoleFormatter, self).__init__("%(message)s")

def format(self, record):
message = super(ConsoleFormatter, self).format(record)
if record.levelname in ["DEBUG", "INFO"]:
return message[0].upper() + message[1:]
else:
return {
"WARNING": "Warning", "ERROR": "Error", "CRITICAL": "Fatal error"
}[record.levelname] + ": " + message[0].lower() + message[1:]

console_formatter = ConsoleFormatter()
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)


from confu.builds import Build
from confu.platform import Platform


def standard_parser(description="Confu configuration script"):
import argparse

from os import linesep
from confu.platform import host, possible_targets

parser = argparse.ArgumentParser(description=description,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("--target", dest="target", metavar="PLATFORM", type=Platform,
default=host.name,
help="platform where the code will run. Potential options:" + linesep +
" " + host.name + " (default)" + linesep +
linesep.join(" " + target for target in possible_targets[1:]))
parser.add_argument("--toolchain", dest="toolchain", metavar="TOOLCHAIN",
choices=["auto", "gnu", "clang"], default="auto",
help="toolchain to use for compilation. Potential options:" + linesep +
linesep.join(" " + name for name in ["auto (default)", "gnu", "clang"]))



return parser
89 changes: 89 additions & 0 deletions confu/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env python

import argparse
import logging


logger = logging.getLogger("confu")


def setup_deps(options, unparsed_args):
import pygit2

import confu.recipes
import types
builtin_recipes = [name for name in confu.recipes.__dict__
if isinstance(confu.recipes.__dict__[name], types.ModuleType)]

preparsed_args = list()
for arg in unparsed_args:
# TODO: more robust checks
if arg.startswith("--with-") and "=" in arg:
preparsed_args += arg.split("=", 1)
else:
preparsed_args.append(arg)

dependency_paths = dict()

import os
for with_arg, path in zip(preparsed_args[0::2], preparsed_args[1::2]):
path = os.path.abspath(os.path.expanduser(os.path.normpath(path)))
dep_name = with_arg[len("--with-"):]
dependency_paths[dep_name] = path

def setup_project_deps(project_dir, root_dir, namespace=""):
import confu.manifest
project = confu.manifest.Project.from_root(project_dir)

deps_dir = os.path.join(root_dir, "deps")
if project.deps and not os.path.isdir(deps_dir):
os.mkdir(deps_dir)

for dep in project.deps:
dep_dir = os.path.join(deps_dir, dep.name)
qualified_name = dep.name if not namespace else namespace + ":" + dep.name
if os.path.exists(dep_dir):
logger.info("reuse dependency {name} from {path}".format(
name=qualified_name, path=dep_dir))
elif dep.name in dependency_paths:
logger.info("link dependency {name} from {path}".format(
name=qualified_name, path=dependency_paths[dep.name]))
os.symlink(dependency_paths[dep.name], dep_dir)
elif dep.url is not None:
logger.info("fetch dependency {name} from {url}".format(
name=qualified_name, url=dep.url))
pygit2.clone_repository(dep.url, dep_dir)
elif dep.name in builtin_recipes:
logger.info("setup dependency {name} using built-in recipe confu.recipes.{name}"
.format(name=qualified_name))
recipe = confu.recipes.__dict__[dep.name]
recipe.setup(dep_dir)
else:
logger.critical("no source provided for dependency {name} ({qname})"
.format(name=dep.name, qname=qualified_name))

setup_project_deps(dep_dir, root_dir,
namespace=dep.name if not namespace else namespace + ":" + dep.name)

import os
root_dir = os.path.abspath(os.getcwd())

setup_project_deps(root_dir, root_dir)


parser = argparse.ArgumentParser(
description="Confu: cross-platform C/C++ configuration system")
subparsers = parser.add_subparsers(title="commands",
description="supported commands")
setup_parser = subparsers.add_parser("setup", help="set up dependencies")
setup_parser.add_argument("args", nargs=argparse.REMAINDER)
setup_parser.set_defaults(process=setup_deps)


def main():
options, unparsed_args = parser.parse_known_args()
options.process(options, unparsed_args)


if __name__ == "__main__":
main()
6 changes: 6 additions & 0 deletions confu/builds/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from confu.builds.base import Build
from confu.builds.unix import UnixBuild
from confu.builds.pnacl import PNaClBuild
from confu.builds.emscripten import EmscriptenBuild
from confu.builds.module import Module, ModuleCollection
from confu.builds.deps import DependencyCollection
158 changes: 158 additions & 0 deletions confu/builds/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import os
import logging

import six

logger = logging.getLogger("confu")

from confu.builds.state import State


class Build(State):
def __init__(self, root_dir, target):
super(Build, self).__init__(root_dir)
if self.__class__ is Build:
raise TypeError("This constructor is intended for use by subclasses only. "
"Use static method Build.from_options to create an abstract Build class")
import confu.platform
assert isinstance(target, confu.platform.Platform)

self.target = target

from confu.manifest import Project
self.manifest = Project.from_root(root_dir)
logger.debug("parsed manifest for " + str(self.manifest))

from confu.builds import DependencyCollection
self.deps = DependencyCollection(root_dir, self.manifest, self.target)

from confu.builds import ModuleCollection
self.modules = ModuleCollection()

from confu.tools import ToolCollection
self.tools = ToolCollection(self.target)

@staticmethod
def from_options(options, root_dir=None, **kwargs):
from confu.utils import get_root_dir
if root_dir is None:
root_dir = get_root_dir()

import confu.globals
if confu.globals.root_dir is None:
confu.globals.root_dir = root_dir
logger.info("detected root directory: " + root_dir)

from confu.platform import host
if options.target.is_emscripten:
from confu.builds import EmscriptenBuild
return EmscriptenBuild(root_dir, options.target, options.toolchain)
elif options.target == host or options.target.is_nacl:
from confu.builds import UnixBuild, PNaClBuild
if options.target.is_pnacl:
return PNaClBuild(root_dir, options.target, options.toolchain)
else:
return UnixBuild(root_dir, options.target, options.toolchain)
else:
raise ValueError("Unsupported target platform {target}".format(target=options.target.name))

@property
def active_module(self):
return self.modules._active

def cc(self, source_path):
raise EnvironmentError("Compilation of C codes is not supported for {target}"
.format(target=options.target.name))

def cxx(self, source_path):
raise EnvironmentError("Compilation of C++ codes is not supported for {target}"
.format(target=options.target.name))

def peachpy(self, source_path):
raise EnvironmentError("Compilation of PeachPy codes is not supported for {target}"
.format(target=options.target.name))

def export_cpath(self, include_dir, include_paths, add_to_include_dirs=True):
from confu.validators import validate_include_dir
include_dir = validate_include_dir(include_dir, self.root_dir)
self.active_module.cpath.append(include_dir)
if add_to_include_dirs:
self._include_dirs.append(include_dir)

def static_library(self, name, object_files):
raise EnvironmentError("Static libraries are not supported for {target}"
.format(target=options.target.name))

def dynamic_library(self, name, object_files):
raise EnvironmentError("Dynamically loaded or shared libraries are not supported on {target}"
.format(target=options.target.name))

def library(self, name, object_files):
raise EnvironmentError("Function libraries are not supported for {target}"
.format(target=options.target.name))

def plugin(self, name, object_files):
raise EnvironmentError("Plugin modules are not supported on {target}"
.format(target=options.target.name))

def executable(self, name, object_files):
import confu.platform
raise EnvironmentError("Executables are not supported on {target}"
.format(target=options.target.name))

def unittest(self, name, object_files):
import confu.platform
if confu.platform.host == options.target:
raise EnvironmentError("Unit tests are not supported on {target}"
.format(target=options.target.name))
else:
raise EnvironmentError("Unit tests are not supported in cross-compilation from {host} to {target}"
.format(host=confu.platform.host, target=options.target.name))

def smoketest(self, name, object_files):
import confu.platform
if confu.platform.host == options.target:
raise EnvironmentError("Smoke tests are not supported on {target}"
.format(target=options.target.name))
else:
raise EnvironmentError("Smoke tests are not supported in cross-compilation from {host} to {target}"
.format(host=confu.platform.host, target=options.target.name))

def benchmark(self, name, object_files):
import confu.platform
if confu.platform.host == options.target:
raise EnvironmentError("Benchmarks are not supported on {target}"
.format(target=options.target.name))
else:
raise EnvironmentError("Benchmarks are not supported in cross-compilation from {host} to {target}"
.format(host=confu.platform.host, target=options.target.name))

def generate(self):
import ninja_syntax
import confu.globals
confu.globals.build_ninja_path = os.path.join(self.root_dir, "build.ninja")
with open(confu.globals.build_ninja_path, "w") as build_ninja:
ninja = ninja_syntax.Writer(build_ninja)
self.generate_variables(ninja)
self.generate_rules(ninja)

import sys
configure_path = os.path.abspath(os.path.normpath(sys.argv[0]))
args = sys.argv[1:]
ninja.rule("configure", configure_path + " $args",
description="CONFIGURE $args", pool="console", generator=True)
ninja.rule("clean", "ninja -f $config -t clean",
description="CLEAN", pool="console")

ninja.build("build.ninja", "configure", configure_path,
variables={"args": " ".join(args)})
ninja.build("clean", "clean",
variables={"config": confu.globals.build_ninja_path})

self.modules._record(ninja)

def generate_variables(self, ninja):
raise NotImplementedError()

def generate_rules(self, ninja):
raise NotImplementedError()
Loading

0 comments on commit 029d7a4

Please sign in to comment.