Example #1
0
def create_build_graph(compiler, linker):
    flags = BuildFlags().O(3).std(17).march("native").fpic()
    # Headers
    utils_h = SourceNode(PATHS["utils.hpp"])
    factorial_h = SourceNode(PATHS["factorial.hpp"], [utils_h])
    fibonacci_h = SourceNode(PATHS["fibonacci.hpp"], [utils_h])
    # Source files
    factorial_cpp = SourceNode(PATHS["factorial.cpp"], [factorial_h],
                               include_dirs=[
                                   PATHS["include"], PATHS["src"],
                                   PATHS["test"], ROOT, TESTS_ROOT
                               ])
    fibonacci_cpp = SourceNode(PATHS["fibonacci.cpp"], [fibonacci_h],
                               include_dirs=[
                                   PATHS["include"], PATHS["src"],
                                   PATHS["test"], ROOT, TESTS_ROOT
                               ])
    test_cpp = SourceNode(PATHS["test.cpp"], [],
                          include_dirs=[
                              PATHS["include"], PATHS["src"], PATHS["test"],
                              ROOT, TESTS_ROOT
                          ])
    # Object files
    factorial_o = CompiledNode(os.path.join(PATHS["build"], "factorial.o"),
                               input=factorial_cpp,
                               compiler=compiler,
                               flags=flags)
    fibonacci_o = CompiledNode(os.path.join(PATHS["build"], "fibonacci.o"),
                               input=fibonacci_cpp,
                               compiler=compiler,
                               flags=flags)
    test_o = CompiledNode(os.path.join(PATHS["build"], "test.o"),
                          input=test_cpp,
                          compiler=compiler,
                          flags=flags)
    # Library and executable
    libmath = LinkedNode(os.path.join(PATHS["build"], "libmath.so"),
                         [factorial_o, fibonacci_o],
                         linker=linker,
                         hashed_path=os.path.join(PATHS["build"],
                                                  "libmath.so"),
                         flags=flags + BuildFlags()._enable_shared(),
                         libs=["stdc++"])
    test = LinkedNode(os.path.join(PATHS["build"], "test"), [test_o, libmath],
                      linker=linker,
                      hashed_path=os.path.join(PATHS["build"], "test"),
                      flags=flags,
                      libs=["stdc++", "math"],
                      lib_dirs=[os.path.dirname(libmath.path)])
    return Graph([
        utils_h, factorial_h, fibonacci_h, factorial_cpp, fibonacci_cpp,
        test_cpp, factorial_o, fibonacci_o, test_o, libmath, test
    ])
Example #2
0
def link_cmd(linker,
             input_paths,
             output_name,
             lib_dirs: List[str] = [],
             flags: BuildFlags = BuildFlags()):
    flags += BuildFlags().O(3).std(17).march("native").fpic()
    # Get output path
    output_path = os.path.join(PATHS["build"], output_name)
    # Generate the command needed
    return linker.link(input_paths,
                       output_path,
                       lib_dirs=lib_dirs,
                       flags=flags), output_path
Example #3
0
def compile_cmd(compiler,
                input_path: str,
                include_dirs: List[str] = [],
                flags: BuildFlags = BuildFlags()):
    flags += BuildFlags().O(3).std(17).march("native").fpic()
    include_dirs = include_dirs or [
        PATHS["include"], PATHS["src"], PATHS["test"], ROOT, TESTS_ROOT
    ]
    # Get output path
    base = os.path.splitext(os.path.basename(input_path))[0]
    output_path = os.path.join(PATHS["build"], f"{base}.o")
    # Generate the command needed
    return compiler.compile(input_path, output_path, include_dirs,
                            flags), output_path
Example #4
0
    def profile(self,
                name: str,
                flags: BuildFlags = BuildFlags(),
                build_dir: str = None,
                file_suffix: str = "") -> Profile:
        f"""
        Returns or creates a profile with the specified parameters.

        :param name: The name of this profile.
        :param flags: The flags to use for this profile. These will be applied to all targets for this profile. Per-target flags always take precedence.
        :param build_dir: The directory to use for build artifacts. Defaults to {os.path.join(self.build_dir, name)}
        :param file_suffix: A file suffix to attach to all artifacts generated for this profile. For example, the default debug profile attaches a ``_debug`` suffix to all library and executable names.

        :returns: :class:`sbuildr.Profile`
        """
        if name not in self.profiles:
            build_dir = self.files.add_writable_dir(
                self.files.add_exclude_dir(
                    os.path.abspath(build_dir
                                    or os.path.join(self.build_dir, name))))
            G_LOGGER.verbose(
                f"Setting build directory for profile: {name} to: {build_dir}")
            self.profiles[name] = Profile(flags=flags,
                                          build_dir=build_dir,
                                          suffix=file_suffix)
        return self.profiles[name]
Example #5
0
 def compile(compiler,
             input_path: str,
             include_dirs: List[str] = [],
             flags: BuildFlags = BuildFlags()):
     cmd, output_path = compile_cmd(compiler, input_path, include_dirs,
                                    flags)
     print(f"Running command: {cmd}")
     subprocess.run(cmd)
     return output_path
Example #6
0
 def signature(
     self,
     input_paths: List[str],
     libs: List[str] = [],
     lib_dirs: List[str] = [],
     flags: BuildFlags = BuildFlags()) -> str:
     # Order of inputs does not matter, but order of libs does.
     sig = [self.ldef.executable()] + list(sorted(
         input_paths)) + self.ldef.parse_flags(flags) + libs + lib_dirs
     return utils.str_hash(sig)
Example #7
0
 def link(linker,
          input_paths,
          output_name,
          lib_dirs: List[str] = [],
          flags: BuildFlags = BuildFlags()):
     cmd, output_path = link_cmd(linker, input_paths, output_name, lib_dirs,
                                 flags)
     print(f"Running command: {cmd}")
     subprocess.run(cmd)
     return output_path
Example #8
0
 def __init__(self,
              path: str,
              input: SourceNode,
              compiler: compiler.Compiler,
              include_dirs: List[str] = [],
              flags: BuildFlags = BuildFlags()):
     super().__init__(path, [input])
     self.compiler = compiler
     # All include directories required for this file.
     self.include_dirs = include_dirs
     self.flags = flags
Example #9
0
    def library(
        self,
        name: str,
        sources: List[str],
        flags: BuildFlags = BuildFlags(),
        libs: List[Union[DependencyLibrary, ProjectTarget, Library]] = [],
        compiler: compiler.Compiler = compiler.clang,
        include_dirs: List[str] = [],
        linker: linker.Linker = linker.clang,
        depends: List[Dependency] = [],
        internal=False,
    ) -> ProjectTarget:
        """
        Adds a library target to all profiles within this project.

        :param name: The name of the target. This should NOT include platform-dependent extensions.
        :param sources: A list of names or paths of source files to include in this target.
        :param flags: Compiler and linker flags. See sbuildr.BuildFlags for details.
        :param libs: A list containing either :class:`ProjectTarget` s, :class:`DependencyLibrary` s or :class:`Library` s.
        :param compiler: The compiler to use for this target. Defaults to clang.
        :param include_dirs: A list of paths for preprocessor include directories. These directories take precedence over automatically deduced include directories.
        :param linker: The linker to use for this target. Defaults to clang.
        :param depends: Any additional dependencies not already captured in libs. This may include header only packages for example.
        :param internal: Whether this target is internal to the project, in which case it will not be installed.

        :returns: :class:`sbuildr.project.target.ProjectTarget`
        """
        self.libraries[name] = self._target(
            name,
            paths.name_to_libname(name),
            sources,
            flags + BuildFlags()._enable_shared(),
            libs,
            compiler,
            include_dirs,
            linker,
            depends,
            internal,
            is_lib=True,
        )
        return self.libraries[name]
Example #10
0
 def __init__(self,
              root: str = None,
              dirs: Set[str] = set(),
              build_dir: str = None):
     self.PROJECT_API_VERSION = Project.PROJECT_API_VERSION
     # The assumption is that the caller of the init function is the SBuildr file for the build.
     config_file = os.path.abspath(inspect.stack()[1][0].f_code.co_filename)
     # Keep track of all files present in project dirs. Since dirs is a set, files is guaranteed
     # to contain no duplicates as well.
     self.files = FileManager(
         root or os.path.abspath(os.path.dirname(config_file)), dirs)
     # The build directory will be writable, and excluded when the FileManager is searching for paths.
     self.build_dir = self.files.add_writable_dir(
         self.files.add_exclude_dir(
             build_dir or os.path.join(self.files.root_dir, "build")))
     # TODO: Make this a parameter?
     self.common_build_dir = os.path.join(self.build_dir, "common")
     # Backend
     self.backend = None
     # Profiles consist of a graph of compiled/linked nodes. Each linked node is a
     # user-defined target for that profile.
     self.profiles: Dict[str, Profile] = {}
     # ProjectTargets combine linked nodes from one or more profiles for each user-defined target.
     # Each ProjectTarget maps profile names to their corresponding linked node for that target.
     self.executables: Dict[str, ProjectTarget] = {}
     self.tests: Dict[str, ProjectTarget] = {}
     self.libraries: Dict[str, ProjectTarget] = {}
     # Files installed by this project.
     self.public_headers: Set[str] = {}
     # Dependencies required for the public headers.
     self.public_header_dependencies: List[Dependency] = []
     # Add default profiles
     self.profile(name="release",
                  flags=BuildFlags().O(3).std(17).march("native").fpic())
     self.profile(
         name="debug",
         flags=BuildFlags().O(0).std(17).debug().fpic().define("S_DEBUG"),
         file_suffix="_debug")
     # A graph describing the entire project. This is typically not constructed until just before the build
     self.graph: Graph = None
Example #11
0
 def __init__(self,
              path: str,
              inputs: List[Node],
              linker: linker.Linker,
              hashed_path: str,
              libs: List[str] = None,
              lib_dirs: List[str] = None,
              flags: BuildFlags = BuildFlags()):
     super().__init__(path=path, libs=libs, lib_dirs=lib_dirs)
     Node.__init__(self, path, inputs)
     self.hashed_path = hashed_path  # The path including hash. self.path is a hard link of this path.
     self.linker = linker
     self.flags = flags
Example #12
0
    def test(
        self,
        name: str,
        sources: List[str],
        flags: BuildFlags = BuildFlags(),
        libs: List[Union[DependencyLibrary, ProjectTarget, Library]] = [],
        compiler: compiler.Compiler = compiler.clang,
        include_dirs: List[str] = [],
        linker: linker.Linker = linker.clang,
        depends: List[Dependency] = [],
    ) -> ProjectTarget:
        """
        Adds an executable target to all profiles within this project. Test targets can be automatically built and run by using the ``test`` command on the CLI.

        :param name: The name of the target. This should NOT include platform-dependent extensions.
        :param sources: A list of names or paths of source files to include in this target.
        :param flags: Compiler and linker flags. See sbuildr.BuildFlags for details.
        :param libs: A list containing either :class:`ProjectTarget` s, :class:`DependencyLibrary` s or :class:`Library` s.
        :param compiler: The compiler to use for this target. Defaults to clang.
        :param include_dirs: A list of paths for preprocessor include directories. These directories take precedence over automatically deduced include directories.
        :param linker: The linker to use for this target. Defaults to clang.
        :param depends: Any additional dependencies not already captured in libs. This may include header only packages for example.

        :returns: :class:`sbuildr.project.target.ProjectTarget`
        """
        self.tests[name] = self._target(
            name,
            paths.name_to_execname(name),
            sources,
            flags,
            libs,
            compiler,
            include_dirs,
            linker,
            depends,
            internal=True,
            is_lib=False,
        )
        return self.tests[name]
Example #13
0
 def link(
     self,
     input_paths: List[str],
     output_path: str,
     libs: List[str] = [],
     lib_dirs: List[str] = [],
     flags: BuildFlags = BuildFlags()) -> List[str]:
     G_LOGGER.debug(f"self.ldef: {self.ldef}")
     linker_flags = self.ldef.parse_flags(flags)
     lib_dirs = [self.ldef.lib_dir(dir) for dir in lib_dirs]
     # In libs, absolute paths are not prepended with the lib prefix (e.g. -l)
     libs = [
         lib if os.path.isabs(lib) else self.ldef.lib(lib) for lib in libs
     ]
     # The full command.
     cmd = [self.ldef.executable()
            ] + input_paths + libs + linker_flags + lib_dirs + [
                self.ldef.output(output_path)
            ]
     G_LOGGER.debug(
         f"Linking: input_paths: {input_paths}, libs: {libs}, linker_flags: {linker_flags}, lib_dirs: {lib_dirs}"
     )
     G_LOGGER.verbose(f"Link Command: {' '.join(cmd)}")
     return cmd
Example #14
0
 def build_libtest(compiler, linker):
     fibonacci = TestLinkers.compile(compiler, PATHS["fibonacci.cpp"])
     factorial = TestLinkers.compile(compiler, PATHS["factorial.cpp"])
     return TestLinkers.link(linker, [fibonacci, factorial, "-lstdc++"],
                             "libtest.so",
                             flags=BuildFlags()._enable_shared())
Example #15
0
 def compile(self, input_path: str, output_path: str, include_dirs: List[str]=[], flags: BuildFlags=BuildFlags()) -> List[str]:
     compiler_flags = self.cdef.parse_flags(flags)
     includes = [self.cdef.include(dir) for dir in include_dirs]
     # The full command, including the output file and the compile-only flag.
     cmd = [self.cdef.executable(), input_path] + compiler_flags + includes + [self.cdef.compile_only(), self.cdef.output(output_path)]
     G_LOGGER.verbose(f"Compile Command: {' '.join(cmd)}")
     return cmd
Example #16
0
 def signature(self, input_path: str, include_dirs: List[str]=[], flags: BuildFlags=BuildFlags()) -> str:
     sig = [self.cdef.executable()] + [input_path] + self.cdef.parse_flags(flags) + include_dirs
     return utils.str_hash(sig)