Skip to content

Commit

Permalink
Merge pull request #127 from Trick-17/feature-public-dependencies
Browse files Browse the repository at this point in the history
Add `public_dependencies`

 - `dependencies` are now private by default
 - `public_dependencies` are visible to dependent targets
  • Loading branch information
GPMueller committed Jul 26, 2021
2 parents 491e2d5 + f61b9f3 commit a9933c4
Show file tree
Hide file tree
Showing 16 changed files with 264 additions and 51 deletions.
14 changes: 13 additions & 1 deletion clang_build/directories.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
class Directories:
def __init__(self, files, public_dependencies):
def __init__(self, files, dependencies, public_dependencies):
"""The root and build directories are taken from `target_description`,
include directories from `files` and `dependencies`.
Include directories are made unique.
"""

self.dependencies = dependencies

# Include directories
self.include_private = files["include_directories"]
self.include_public = files["public_include_directories"]
Expand All @@ -8,6 +16,10 @@ def __init__(self, files, public_dependencies):
# if self.root_directory.joinpath('include').exists():
# self._include_directories_public = [self.root_directory.joinpath('include')] + self._include_directories_public

# Public include directories of private dependencies are applied
for target in dependencies:
self.include_private += target.directories.include_public

# Public include directories of public dependencies are forwarded
for target in public_dependencies:
self.include_public += target.directories.include_public
Expand Down
22 changes: 16 additions & 6 deletions clang_build/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,12 @@ def add_targets(self, target_list: list):
# Add edges for dependencies in targets defined in project
for target in target_list:
target.config["dependencies"] = self._get_dependencies_2(target, target.config.get("dependencies", []))
target.config["public_dependencies"] = self._get_dependencies_2(target, target.config.get("public_dependencies", []))

for dependency in target.config["dependencies"]:
self._project_tree.add_edge(target, dependency)
for dependency in target.config["public_dependencies"]:
self._project_tree.add_edge(target, dependency, public=True)

# Create new dotfile with full dependency graph
if create_dotfile:
Expand Down Expand Up @@ -622,9 +626,15 @@ def _get_dependencies(self, target_description):
for dependency in self._project_tree.successors(target_description)
]

public_dependencies = []
successors = self._project_tree.successors(target_description)
for target, dependency, public in self._project_tree.subgraph([target_description] + [s for s in successors]).edges(data="public"):
if public == True:
public_dependencies.append(self._project_tree.nodes()[dependency]["data"])

# Are there executables named as dependencies?
executable_dependencies = [
target for target in dependencies if isinstance(target, _Executable)
target for target in dependencies+public_dependencies if isinstance(target, _Executable)
]
if executable_dependencies:
exelist = ", ".join([f"[{dep.name}]" for dep in executable_dependencies])
Expand All @@ -634,7 +644,7 @@ def _get_dependencies(self, target_description):
self._logger.error(error_message)
raise RuntimeError(error_message)

return dependencies
return dependencies, public_dependencies

def _get_dependencies_2(self, target, names):
dependencies = []
Expand Down Expand Up @@ -679,7 +689,7 @@ def _target_from_description(self, target_description):
Returns the correct target type based on input parameters
"""

dependencies = self._get_dependencies(target_description)
dependencies, public_dependencies = self._get_dependencies(target_description)

# Sources
target_description.get_sources()
Expand All @@ -696,7 +706,7 @@ def _target_from_description(self, target_description):
if target_type is not None:
target_type = str(target_type).lower()
if target_type in _TARGET_MAP:
return _TARGET_MAP[target_type](target_description, files, dependencies)
return _TARGET_MAP[target_type](target_description, files, dependencies, public_dependencies)
else:
error_message = target_description.log_message(
f'ERROR: Unsupported target type: "{target_description.config["target_type"].lower()}"'
Expand All @@ -710,12 +720,12 @@ def _target_from_description(self, target_description):
target_description.log_message(
"no source files found. Creating header-only target."
)
return _HeaderOnly(target_description, files, dependencies)
return _HeaderOnly(target_description, files, dependencies, public_dependencies)

target_description.log_message(
f'{len(files["sourcefiles"])} source file(s) found. Creating executable target.'
)
return _Executable(target_description, files, dependencies)
return _Executable(target_description, files, dependencies, public_dependencies)

def get_sources(self):
"""External sources, if present, will be downloaded to build_directory/external_sources.
Expand Down
68 changes: 46 additions & 22 deletions clang_build/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ def dependencies(self):
"""
return self._dependencies

@property
def public_dependencies(self):
"""Return a list of any:`clang_build.target.Target`, which this
target depends on.
"""
return self._public_dependencies

@property
def root_directory(self):
"""Folders "include", "src", etc. are searched
Expand Down Expand Up @@ -80,7 +87,7 @@ def directories(self):
"""
return self._directories

def __init__(self, target_description, files, dependencies=None):
def __init__(self, target_description, files, dependencies=None, public_dependencies=None):
"""Initialise a target.
The procedure for initialisation is:
Expand All @@ -100,6 +107,9 @@ def __init__(self, target_description, files, dependencies=None):
dependencies
Optional. A list of any:`clang_build.target.Target` which this target
depends on.
public_dependencies
Optional. A list of any:`clang_build.target.Target` which this target
depends on and which should also be available to dependent targets.
"""
_NamedLogger.__init__(self, _LOGGER)
self._name = target_description.name
Expand All @@ -110,19 +120,22 @@ def __init__(self, target_description, files, dependencies=None):
dependencies = []
self._dependencies = dependencies

if public_dependencies is None:
public_dependencies = []
self._public_dependencies = public_dependencies

self._root_directory = _Path(target_description.root_directory)
self._build_directory = target_description.build_directory

self._headers = list(dict.fromkeys(files["headers"]))

self._directories = Directories(files, self.dependencies)
self._directories = Directories(files, self.dependencies, self.public_dependencies)

# Compile and link flags
self._build_flags = self._get_default_flags()

# Dependencies' flags
for target in self.dependencies:
# Header only libraries will forward all non-private flags
for target in self.dependencies + self.public_dependencies:
self._add_dependency_flags(target)

self._build_flags.add_target_flags(self._environment.toolchain.platform, target_description.config)
Expand Down Expand Up @@ -169,7 +182,7 @@ def bundle(self):
"""
self.unsuccessful_bundle = False
bundle_files = []
for dependency in self.dependencies:
for dependency in self.dependencies + self.public_dependencies:
bundle_files += dependency.bundle()
return bundle_files

Expand Down Expand Up @@ -200,15 +213,16 @@ class HeaderOnly(Target):
TODO: need to check whether "public" makes sense for header-only, when we have implemented "private" dependencies
"""

def __init__(self, target_description, files, dependencies=None):
def __init__(self, target_description, files, dependencies=None, public_dependencies=None):
"""Initialise a header-only target.
Header-only targets' private flags and include-directories are public.
"""
super().__init__(
target_description=target_description,
files=files,
dependencies=dependencies
dependencies=dependencies,
public_dependencies=public_dependencies
)

if files["sourcefiles"]:
Expand All @@ -234,6 +248,7 @@ def _get_default_flags(self):
def _add_dependency_flags(self, target):
"""Forward dependencies' public and interface flags.
"""
# Header only libraries will forward all non-private flags
self._build_flags.forward_public_flags(target)
self._build_flags.forward_interface_flags(target)

Expand All @@ -260,7 +275,8 @@ def __init__(
platform_flags,
prefix,
suffix,
dependencies=None
dependencies=None,
public_dependencies=None
):
self.source_files = files["sourcefiles"]
self.is_c_target = not any(
Expand All @@ -270,7 +286,8 @@ def __init__(
super().__init__(
target_description=target_description,
files=files,
dependencies=dependencies
dependencies=dependencies,
public_dependencies=public_dependencies
)

if not self.source_files:
Expand Down Expand Up @@ -391,7 +408,7 @@ class Executable(Compilable):
An executable cannot be the dependency of another target.
"""

def __init__(self, target_description, files, dependencies=None):
def __init__(self, target_description, files, dependencies=None, public_dependencies=None):
"""Initialise an executable target.
"""

Expand All @@ -402,7 +419,8 @@ def __init__(self, target_description, files, dependencies=None):
platform_flags=target_description.environment.toolchain.platform_defaults['PLATFORM_EXTRA_FLAGS_EXECUTABLE'],
prefix=target_description.environment.toolchain.platform_defaults['EXECUTABLE_PREFIX'],
suffix=target_description.environment.toolchain.platform_defaults['EXECUTABLE_SUFFIX'],
dependencies=dependencies
dependencies=dependencies,
public_dependencies=public_dependencies
)

### Bundling requires extra flags
Expand All @@ -414,7 +432,7 @@ def bundle(self):

### Gather
bundle_files = []
for dependency in self.dependencies:
for dependency in self.dependencies + self.public_dependencies:
bundle_files += dependency.bundle()

### Copy
Expand Down Expand Up @@ -512,8 +530,8 @@ def link(self):
[buildable.object_file for buildable in self.buildables],
self.outfile,
self._build_flags._language_flags() + self._build_flags.final_link_flags_list(),
[target.output_folder.resolve() for target in self.dependencies if target.__class__ is not HeaderOnly],
[target.outname for target in self.dependencies if target.__class__ is not HeaderOnly],
[target.output_folder.resolve() for target in self.dependencies+self.public_dependencies if target.__class__ is not HeaderOnly],
[target.outname for target in self.dependencies+self.public_dependencies if target.__class__ is not HeaderOnly],
False,
self.is_c_target)

Expand All @@ -525,7 +543,9 @@ def link(self):
{self.identifier: self.link_report})

class SharedLibrary(Compilable):
def __init__(self, target_description, files, dependencies=None):
def __init__(self, target_description, files, dependencies=None, public_dependencies=None):
"""Initialise a shared library target.
"""

super().__init__(
target_description=target_description,
Expand All @@ -534,7 +554,8 @@ def __init__(self, target_description, files, dependencies=None):
platform_flags=target_description.environment.toolchain.platform_defaults['PLATFORM_EXTRA_FLAGS_SHARED'],
prefix=target_description.environment.toolchain.platform_defaults['SHARED_LIBRARY_PREFIX'],
suffix=target_description.environment.toolchain.platform_defaults['SHARED_LIBRARY_SUFFIX'],
dependencies=dependencies
dependencies=dependencies,
public_dependencies=public_dependencies
)

# TODO: This has to go to the flags department I guess
Expand All @@ -558,7 +579,7 @@ def bundle(self):
self_bundle_files.append(_Path(str(self.outfile)[:-3] + "lib"))

bundle_files = []
for dependency in self.dependencies:
for dependency in self.dependencies + self.public_dependencies:
bundle_files += dependency.bundle()

### Copy
Expand Down Expand Up @@ -593,8 +614,8 @@ def link(self):
[buildable.object_file for buildable in self.buildables],
self.outfile,
self._build_flags._language_flags() + self._build_flags.final_link_flags_list(),
[target.output_folder.resolve() for target in self.dependencies if target.__class__ is not HeaderOnly],
[target.outname for target in self.dependencies if target.__class__ is not HeaderOnly],
[target.output_folder.resolve() for target in self.dependencies+self.public_dependencies if target.__class__ is not HeaderOnly],
[target.outname for target in self.dependencies+self.public_dependencies if target.__class__ is not HeaderOnly],
True,
self.is_c_target)

Expand All @@ -607,7 +628,9 @@ def link(self):


class StaticLibrary(Compilable):
def __init__(self, target_description, files, dependencies=None):
def __init__(self, target_description, files, dependencies=None, public_dependencies=None):
"""Initialise a static library target.
"""

super().__init__(
target_description=target_description,
Expand All @@ -616,7 +639,8 @@ def __init__(self, target_description, files, dependencies=None):
platform_flags=target_description.environment.toolchain.platform_defaults['PLATFORM_EXTRA_FLAGS_STATIC'],
prefix=target_description.environment.toolchain.platform_defaults['STATIC_LIBRARY_PREFIX'],
suffix=target_description.environment.toolchain.platform_defaults['STATIC_LIBRARY_SUFFIX'],
dependencies=dependencies
dependencies=dependencies,
public_dependencies=public_dependencies
)

def _add_dependency_flags(self, target):
Expand All @@ -637,7 +661,7 @@ def link(self):
objects = [buildable.object_file for buildable in self.buildables]

# Dependencies' objects
for target in self.dependencies:
for target in self.dependencies+self.public_dependencies:
if not target.__class__ is HeaderOnly:
objects += [buildable.object_file for buildable in target.buildables]

Expand Down
Loading

0 comments on commit a9933c4

Please sign in to comment.