diff --git a/llvm/test/LLVMBuild/optional-library/Input/LLVMBuild.txt b/llvm/test/LLVMBuild/optional-library/Input/LLVMBuild.txt new file mode 100644 --- /dev/null +++ b/llvm/test/LLVMBuild/optional-library/Input/LLVMBuild.txt @@ -0,0 +1,48 @@ +;===- ./LLVMBuild.txt ------------------------------------------*- Conf -*--===; +; +; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +; See https://llvm.org/LICENSE.txt for license information. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[common] +subdirectories = lib + +[component_0] +type = LibraryGroup +name = Engine +parent = $ROOT + +[component_1] +type = Library +name = Interpreter +parent = $ROOT + +[component_2] +type = Group +name = Miscellaneous +parent = $ROOT + +[component_3] +type = LibraryGroup +name = Native +parent = $ROOT + +[component_4] +type = LibraryGroup +name = NativeCodeGen +parent = $ROOT + +[component_5] +type = LibraryGroup +name = all-targets +parent = $ROOT diff --git a/llvm/test/LLVMBuild/optional-library/Input/lib/IR/Function.cpp b/llvm/test/LLVMBuild/optional-library/Input/lib/IR/Function.cpp new file mode 100644 diff --git a/llvm/test/LLVMBuild/optional-library/Input/lib/LLVMBuild.txt b/llvm/test/LLVMBuild/optional-library/Input/lib/LLVMBuild.txt new file mode 100644 --- /dev/null +++ b/llvm/test/LLVMBuild/optional-library/Input/lib/LLVMBuild.txt @@ -0,0 +1,44 @@ +;===- ./LLVMBuild.txt ------------------------------------------*- Conf -*--===; +; +; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +; See https://llvm.org/LICENSE.txt for license information. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = OptionalLibrary +name = IncludeBecauseEnabled +enabled_component = 1 +parent = $ROOT + +[component_1] +type = OptionalLibrary +name = IncludeBecauseRequired +; OptionalLibrary being required via dependencies should be a warning though +parent = $ROOT + +[component_2] +type = Library +name = IncludeBecauseNotOptional +parent = $ROOT + +[component_3] +type = OptionalLibrary +name = ExcludeBecauseDisabled +parent = $ROOT + +[component_4] +type = Library +name = Bar +required_libraries = IncludeBecauseRequired IncludeBecauseNotOptional +optional_libraries = IncludeBecauseEnabled ExcludeBecauseDisabled +parent = $ROOT diff --git a/llvm/test/LLVMBuild/optional-library/optional-library.test b/llvm/test/LLVMBuild/optional-library/optional-library.test new file mode 100644 --- /dev/null +++ b/llvm/test/LLVMBuild/optional-library/optional-library.test @@ -0,0 +1,33 @@ +RUN: %llvmbuild --source-root=%S/Input --write-llvmbuild=%T 2>%T/stderr.txt +RUN: FileCheck %s < %T/lib/LLVMBuild.txt +RUN: FileCheck --check-prefix=STDERR %s < %T/stderr.txt + +CHECK: [component_0] +CHECK-NEXT: type = Library +CHECK-NEXT: name = Bar +CHECK-NEXT: parent = $ROOT +CHECK-NEXT: required_libraries = IncludeBecauseRequired IncludeBecauseNotOptional IncludeBecauseEnabled +CHECK-NEXT: optional_libraries = IncludeBecauseEnabled ExcludeBecauseDisabled + +CHECK: [component_1] +CHECK-NEXT: type = OptionalLibrary +CHECK-NEXT: name = ExcludeBecauseDisabled +CHECK-NEXT: parent = $ROOT + +CHECK: [component_2] +CHECK-NEXT: type = OptionalLibrary +CHECK-NEXT: name = IncludeBecauseEnabled +CHECK-NEXT: parent = $ROOT +CHECK-NEXT: enabled_component = 1 + +CHECK: [component_3] +CHECK-NEXT: type = Library +CHECK-NEXT: name = IncludeBecauseNotOptional +CHECK-NEXT: parent = $ROOT + +CHECK: [component_4] +CHECK-NEXT: type = OptionalLibrary +CHECK-NEXT: name = IncludeBecauseRequired +CHECK-NEXT: parent = $ROOT + +STDERR: llvm-build: warning: optional component IncludeBecauseRequired required by Bar diff --git a/llvm/utils/llvm-build/llvmbuild/componentinfo.py b/llvm/utils/llvm-build/llvmbuild/componentinfo.py --- a/llvm/utils/llvm-build/llvmbuild/componentinfo.py +++ b/llvm/utils/llvm-build/llvmbuild/componentinfo.py @@ -119,6 +119,7 @@ kwargs = ComponentInfo.parse_items(items) kwargs['library_name'] = items.get_optional_string('library_name') kwargs['required_libraries'] = items.get_list('required_libraries') + kwargs['optional_libraries'] = items.get_list('optional_libraries') kwargs['add_to_library_groups'] = items.get_list( 'add_to_library_groups') kwargs['installed'] = items.get_optional_bool('installed', True) @@ -130,7 +131,8 @@ return LibraryComponentInfo(subpath, **kwargs) def __init__(self, subpath, name, dependencies, parent, library_name, - required_libraries, add_to_library_groups, installed): + required_libraries, optional_libraries, add_to_library_groups, + installed): ComponentInfo.__init__(self, subpath, name, dependencies, parent) # If given, the name to use for the library instead of deriving it from @@ -141,6 +143,10 @@ # with this component. self.required_libraries = list(required_libraries) + # The names of the library components which are optional when linking + # with this component. + self.optional_libraries = list(optional_libraries) + # The names of the library group components this component should be # considered part of. self.add_to_library_groups = list(add_to_library_groups) @@ -153,6 +159,8 @@ yield r for r in self.required_libraries: yield ('required library', r) + for r in self.optional_libraries: + yield ('optional library', r) for r in self.add_to_library_groups: yield ('library group', r) @@ -167,6 +175,9 @@ if self.required_libraries: result += 'required_libraries = %s\n' % ' '.join( self.required_libraries) + if self.optional_libraries: + result += 'optional_libraries = %s\n' % ' '.join( + self.optional_libraries) if self.add_to_library_groups: result += 'add_to_library_groups = %s\n' % ' '.join( self.add_to_library_groups) @@ -208,11 +219,12 @@ return OptionalLibraryComponentInfo(subpath, **kwargs) def __init__(self, subpath, name, dependencies, parent, library_name, - required_libraries, add_to_library_groups, installed, - enabled_component): + required_libraries, optional_libraries, add_to_library_groups, + installed, enabled_component): LibraryComponentInfo.__init__(self, subpath, name, dependencies, parent, library_name, required_libraries, - add_to_library_groups, installed) + optional_libraries, add_to_library_groups, + installed) self.enabled_component = enabled_component self.optional_component = True @@ -230,18 +242,23 @@ def parse(subpath, items): kwargs = ComponentInfo.parse_items(items, has_dependencies = False) kwargs['required_libraries'] = items.get_list('required_libraries') + kwargs['optional_libraries'] = items.get_list('optional_libraries') kwargs['add_to_library_groups'] = items.get_list( 'add_to_library_groups') return LibraryGroupComponentInfo(subpath, **kwargs) def __init__(self, subpath, name, parent, required_libraries = [], - add_to_library_groups = []): + optional_libraries = [], add_to_library_groups = []): ComponentInfo.__init__(self, subpath, name, [], parent) # The names of the library components which are required when linking # with this component. self.required_libraries = list(required_libraries) + # The names of the library components which are optional when linking + # with this component. + self.optional_libraries = list(optional_libraries) + # The names of the library group components this component should be # considered part of. self.add_to_library_groups = list(add_to_library_groups) @@ -251,6 +268,8 @@ yield r for r in self.required_libraries: yield ('required library', r) + for r in self.optional_libraries: + yield ('optional library', r) for r in self.add_to_library_groups: yield ('library group', r) @@ -263,6 +282,9 @@ if self.required_libraries and not self._is_special_group: result += 'required_libraries = %s\n' % ' '.join( self.required_libraries) + if self.optional_libraries and not self._is_special_group: + result += 'optional_libraries = %s\n' % ' '.join( + self.optional_libraries) if self.add_to_library_groups: result += 'add_to_library_groups = %s\n' % ' '.join( self.add_to_library_groups) @@ -278,6 +300,7 @@ def parse(subpath, items): kwargs = ComponentInfo.parse_items(items, has_dependencies = False) kwargs['required_libraries'] = items.get_list('required_libraries') + kwargs['optional_libraries'] = items.get_list('optional_libraries') kwargs['add_to_library_groups'] = items.get_list( 'add_to_library_groups') kwargs['has_jit'] = items.get_optional_bool('has_jit', False) @@ -290,8 +313,8 @@ return TargetGroupComponentInfo(subpath, **kwargs) def __init__(self, subpath, name, parent, required_libraries = [], - add_to_library_groups = [], has_jit = False, - has_asmprinter = False, has_asmparser = False, + optional_libraries = [], add_to_library_groups = [], + has_jit = False, has_asmprinter = False, has_asmparser = False, has_disassembler = False): ComponentInfo.__init__(self, subpath, name, [], parent) @@ -299,6 +322,10 @@ # with this component. self.required_libraries = list(required_libraries) + # The names of the library components which are optional when linking + # with this component. + self.optional_libraries = list(optional_libraries) + # The names of the library group components this component should be # considered part of. self.add_to_library_groups = list(add_to_library_groups) @@ -324,6 +351,8 @@ yield r for r in self.required_libraries: yield ('required library', r) + for r in self.optional_libraries: + yield ('optional library', r) for r in self.add_to_library_groups: yield ('library group', r) @@ -336,6 +365,9 @@ if self.required_libraries: result += 'required_libraries = %s\n' % ' '.join( self.required_libraries) + if self.optional_libraries: + result += 'optional_libraries = %s\n' % ' '.join( + self.optional_libraries) if self.add_to_library_groups: result += 'add_to_library_groups = %s\n' % ' '.join( self.add_to_library_groups) @@ -355,30 +387,41 @@ def parse(subpath, items): kwargs = ComponentInfo.parse_items(items) kwargs['required_libraries'] = items.get_list('required_libraries') + kwargs['optional_libraries'] = items.get_list('optional_libraries') return ToolComponentInfo(subpath, **kwargs) def __init__(self, subpath, name, dependencies, parent, - required_libraries): + required_libraries, optional_libraries): ComponentInfo.__init__(self, subpath, name, dependencies, parent) # The names of the library components which are required to link this # tool. self.required_libraries = list(required_libraries) + # The names of the library components which are required to link this + # tool. + self.optional_libraries = list(optional_libraries) + def get_component_references(self): for r in ComponentInfo.get_component_references(self): yield r for r in self.required_libraries: yield ('required library', r) + for r in self.optional_libraries: + yield ('optional library', r) def get_llvmbuild_fragment(self): - return """\ + result = """\ type = %s name = %s parent = %s required_libraries = %s """ % (self.type_name, self.name, self.parent, ' '.join(self.required_libraries)) + if self.optional_libraries: + result += 'optional_libraries = %s\n' % ' '.join( + self.optional_libraries) + return result class BuildToolComponentInfo(ToolComponentInfo): type_name = 'BuildTool' @@ -387,6 +430,7 @@ def parse(subpath, items): kwargs = ComponentInfo.parse_items(items) kwargs['required_libraries'] = items.get_list('required_libraries') + kwargs['optional_libraries'] = items.get_list('optional_libraries') return BuildToolComponentInfo(subpath, **kwargs) ### diff --git a/llvm/utils/llvm-build/llvmbuild/main.py b/llvm/utils/llvm-build/llvmbuild/main.py --- a/llvm/utils/llvm-build/llvmbuild/main.py +++ b/llvm/utils/llvm-build/llvmbuild/main.py @@ -5,7 +5,7 @@ import llvmbuild.componentinfo as componentinfo -from llvmbuild.util import fatal, note +from llvmbuild.util import fatal, warning, note ### @@ -96,6 +96,39 @@ self.component_info_map = None self.ordered_component_infos = None + def warn_if_optional_component_required(self): + """warn_if_optional_component_required() -> None + + Warn if optional components are not actually optional because they're + included in required_libraries. + """ + component_map = dict((ci.name, ci) for ci in self.component_infos) + for ci in self.component_infos: + if not hasattr(ci, 'required_libraries'): + continue + for libname in ci.required_libraries: + if component_map[libname].optional_component: + warning('optional component %s required by %s' % (libname, + ci.name)) + + def promote_optional_dependencies(self): + """promote_optional_dependencies() -> None + + Optional dependencies that are satisfied are promoted to required + dependencies when emitting the generated outputs + """ + component_map = dict((ci.name, ci) for ci in self.component_infos) + for ci in self.component_infos: + if not hasattr(ci, 'optional_libraries'): + continue + libnames = list(ci.optional_libraries) + for libname in libnames: + if component_map[libname].enabled_component: + if libname not in ci.required_libraries: + ci.required_libraries.append(libname) + else: + ci.optional_libraries.append(libname) + def validate_components(self): """validate_components() -> None @@ -835,6 +868,9 @@ else: parser.error('component `%s` is not optional' % c.name) + project_info.warn_if_optional_component_required() + project_info.promote_optional_dependencies() + # Validate the project component info. project_info.validate_components()