diff options
author | 2015-12-10 13:48:35 +0000 | |
---|---|---|
committer | 2015-12-10 20:15:38 +0000 | |
commit | 20262fdf40be0ba7fc74cb03d552cadebd76af6d (patch) | |
tree | f9d19b486b6fc71f43d8270e709d3c74f0a8a05d /src/test/java/com | |
parent | 76d81db677e07ec21d21789c624bd85834cc9b21 (diff) |
Open-source MockCcSupport and some tests for LibraryLinkingTest.
Also changed the setup of BazelAnalysisMock.
--
MOS_MIGRATED_REVID=109890009
Diffstat (limited to 'src/test/java/com')
11 files changed, 795 insertions, 53 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD index 8f2d50ce3a..e9e7afb4b4 100644 --- a/src/test/java/com/google/devtools/build/lib/BUILD +++ b/src/test/java/com/google/devtools/build/lib/BUILD @@ -535,9 +535,11 @@ java_library( "//src/main/java/com/google/devtools/build/lib:runtime", "//src/main/java/com/google/devtools/build/lib:util", "//src/main/java/com/google/devtools/build/lib:vfs", + "//src/main/java/com/google/devtools/build/lib/actions", "//src/main/java/com/google/devtools/build/skyframe", "//src/main/java/com/google/devtools/common/options", "//src/main/protobuf:build_proto", + "//src/main/protobuf:crosstool_config_proto", "//src/main/protobuf:extra_actions_base_proto", "//third_party:guava", "//third_party:guava-testlib", @@ -926,12 +928,14 @@ java_test( "//src/main/java/com/google/devtools/build/lib:bazel-rules", "//src/main/java/com/google/devtools/build/lib:build-base", "//src/main/java/com/google/devtools/build/lib:cmdline", + "//src/main/java/com/google/devtools/build/lib:common", "//src/main/java/com/google/devtools/build/lib:util", "//src/main/java/com/google/devtools/build/lib:vfs", "//src/main/java/com/google/devtools/build/lib/actions", "//src/main/java/com/google/devtools/build/lib/rules/cpp", "//src/main/java/com/google/devtools/common/options", "//src/main/protobuf:crosstool_config_proto", + "//src/test/java/com/google/devtools/build/lib:packages_testutil", "//third_party:guava", "//third_party:guava-testlib", "//third_party:jsr305", diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java index 619ba2ff52..98d5a12b89 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java @@ -29,6 +29,8 @@ import com.google.devtools.build.lib.bazel.rules.BazelRuleClassProvider; import com.google.devtools.build.lib.bazel.rules.python.BazelPythonConfiguration; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.RuleClass; +import com.google.devtools.build.lib.packages.util.BazelMockCcSupport; +import com.google.devtools.build.lib.packages.util.MockCcSupport; import com.google.devtools.build.lib.packages.util.MockToolsConfig; import com.google.devtools.build.lib.rules.android.AndroidConfiguration; import com.google.devtools.build.lib.rules.apple.AppleConfiguration; @@ -57,11 +59,11 @@ public final class BazelAnalysisMock extends AnalysisMock { @Override public void setupMockClient(MockToolsConfig config) throws IOException { - String workspace = config.getPath("").getPathString(); + String bazelToolWorkspace = config.getPath("/bazel_tools_workspace").getPathString(); ArrayList<String> workspaceContents = new ArrayList<>( ImmutableList.of( - "local_repository(name = 'bazel_tools', path = '" + workspace + "')", + "local_repository(name = 'bazel_tools', path = '" + bazelToolWorkspace + "')", "bind(", " name = 'objc_proto_lib',", " actual = '//objcproto:ProtocolBuffers_lib',", @@ -70,12 +72,12 @@ public final class BazelAnalysisMock extends AnalysisMock { " name = 'objc_proto_cpp_lib',", " actual = '//objcproto:ProtocolBuffersCPP_lib',", ")", - "bind(name = 'android/sdk', actual='//tools/android:sdk')", - "bind(name = 'tools/cpp', actual='//tools/cpp')", + "bind(name = 'android/sdk', actual='@bazel_tools//tools/android:sdk')", "bind(name = 'tools/python', actual='//tools/python')")); config.overwrite("WORKSPACE", workspaceContents.toArray(new String[workspaceContents.size()])); - config.create("tools/jdk/BUILD", + config.create( + "/bazel_tools_workspace/tools/jdk/BUILD", "package(default_visibility=['//visibility:public'])", "java_toolchain(name = 'toolchain', encoding = 'UTF-8', source_version = '8', ", " target_version = '8')", @@ -89,48 +91,17 @@ public final class BazelAnalysisMock extends AnalysisMock { "filegroup(name='java', srcs = ['jdk/jre/bin/java', 'dummy'])", "exports_files(['JavaBuilder_deploy.jar','SingleJar_deploy.jar',", " 'JavaBuilderCanary_deploy.jar', 'ijar', 'GenClass_deploy.jar'])"); - config.create("tools/cpp/BUILD", - "cc_library(name = 'stl')", - "cc_library(name = 'malloc')", - "filegroup(name = 'toolchain', ", - " srcs = [':cc-compiler-local', ':cc-compiler-darwin', ':cc-compiler-piii',", - " ':cc-compiler-armeabi-v7a', ':empty'],", - ")", - "cc_toolchain(name = 'cc-compiler-k8', all_files = ':empty', compiler_files = ':empty',", - " cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ", - " linker_files = ':empty',", - " objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',", - ")", - "cc_toolchain(name = 'cc-compiler-piii', all_files = ':empty', compiler_files = ':empty',", - " cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ", - " linker_files = ':empty',", - " objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',", - ")", - "cc_toolchain(name = 'cc-compiler-darwin', all_files = ':empty', ", - " compiler_files = ':empty',", - " cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ", - " linker_files = ':empty',", - " objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',", - ")", - "cc_toolchain(name = 'cc-compiler-armeabi-v7a', all_files = ':empty', ", - " compiler_files = ':empty',", - " cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ", - " linker_files = ':empty',", - " objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',", - ")"); - config.create( - "tools/cpp/CROSSTOOL", readFromResources("com/google/devtools/build/lib/MOCK_CROSSTOOL")); ImmutableList<String> androidBuildContents = createAndroidBuildContents(); config.create( - "tools/android/BUILD", + "/bazel_tools_workspace/tools/android/BUILD", androidBuildContents.toArray(new String[androidBuildContents.size()])); - config.create("tools/genrule/BUILD", - "exports_files(['genrule-setup.sh'])"); config.create( - "third_party/java/jarjar/BUILD", + "/bazel_tools_workspace/tools/genrule/BUILD", "exports_files(['genrule-setup.sh'])"); + config.create( + "/bazel_tools_workspace/third_party/java/jarjar/BUILD", "package(default_visibility=['//visibility:public'])", "licenses(['notice'])", "java_binary(name = 'jarjar_bin',", @@ -139,13 +110,14 @@ public final class BazelAnalysisMock extends AnalysisMock { "java_import(name = 'jarjar_import',", " jars = [ 'jarjar.jar' ])"); - config.create("tools/test/BUILD", "filegroup(name = 'runtime')"); + config.create("/bazel_tools_workspace/tools/test/BUILD", "filegroup(name = 'runtime')"); config.create( - "tools/python/BUILD", + "/bazel_tools_workspace/tools/python/BUILD", "package(default_visibility=['//visibility:public'])", "exports_files(['precompile.py'])", "sh_binary(name='2to3', srcs=['2to3.sh'])"); + ccSupport().setup(config); } private ImmutableList<String> createAndroidBuildContents() { @@ -257,4 +229,9 @@ public final class BazelAnalysisMock extends AnalysisMock { public ImmutableList<Class<? extends FragmentOptions>> getBuildOptions() { return BazelRuleClassProvider.BUILD_OPTIONS; } + + @Override + public MockCcSupport ccSupport() { + return BazelMockCcSupport.INSTANCE; + } } diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java index dbe8f37ff8..ec577ebd67 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java @@ -19,6 +19,7 @@ import com.google.devtools.build.lib.analysis.BlazeDirectories; import com.google.devtools.build.lib.analysis.ConfigurationCollectionFactory; import com.google.devtools.build.lib.analysis.config.ConfigurationFactory; import com.google.devtools.build.lib.analysis.config.FragmentOptions; +import com.google.devtools.build.lib.packages.util.MockCcSupport; import com.google.devtools.build.lib.packages.util.MockToolsConfig; import com.google.devtools.build.lib.rules.repository.LocalRepositoryFunction; import com.google.devtools.build.lib.rules.repository.LocalRepositoryRule; @@ -70,6 +71,12 @@ public abstract class AnalysisMock { public abstract ImmutableList<Class<? extends FragmentOptions>> getBuildOptions(); + public abstract MockCcSupport ccSupport(); + + public void setupCcSupport(MockToolsConfig config) throws IOException { + get().ccSupport().setup(config); + } + public ImmutableMap<SkyFunctionName, SkyFunction> getSkyFunctions(BlazeDirectories directories) { // Some tests require the local_repository rule so we need the appropriate SkyFunctions. RepositoryFunction localRepositoryFunction = new LocalRepositoryFunction(); @@ -110,6 +117,11 @@ public abstract class AnalysisMock { } @Override + public MockCcSupport ccSupport() { + return delegate.ccSupport(); + } + + @Override public Collection<String> getOptionOverrides() { return delegate.getOptionOverrides(); } diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockCcSupport.java b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockCcSupport.java new file mode 100644 index 0000000000..fde98b6f0a --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockCcSupport.java @@ -0,0 +1,130 @@ +// Copyright 2015 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.packages.util; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Bazel implementation of {@link MockCcSupport} + */ +public final class BazelMockCcSupport extends MockCcSupport { + + public static final BazelMockCcSupport INSTANCE = new BazelMockCcSupport(); + /** Filter to remove implicit dependencies of C/C++ rules. */ + private static final Predicate<String> CC_LABEL_NAME_FILTER = + new Predicate<String>() { + @Override + public boolean apply(String label) { + return !label.startsWith("@blaze_tools//tools/cpp/stl"); + } + }; + + private BazelMockCcSupport() {} + + private static final ImmutableList<String> CROSSTOOL_ARCHS = + ImmutableList.of("piii", "k8", "armeabi-v7a"); + + protected static void createBasePackage(MockToolsConfig config) throws IOException { + config.create( + "base/BUILD", + "package(default_visibility=['//visibility:public'])", + "cc_library(name = 'system_malloc', linkstatic = 1)", + "cc_library(name = 'base', srcs=['timestamp.h'])"); + if (config.isRealFileSystem()) { + config.linkTool("base/timestamp.h"); + } else { + config.create("base/timestamp.h", ""); + } + } + + protected String getRealFilesystemCrosstoolTopPath() { + assert false; + return null; + } + + protected String[] getRealFilesystemTools(String crosstoolTop) { + assert false; + return null; + } + + protected ImmutableList<String> getCrosstoolArchs() { + return CROSSTOOL_ARCHS; + } + + @Override + public void setup(MockToolsConfig config) throws IOException { + config.create( + "/bazel_tools_workspace/tools/cpp/BUILD", + "cc_library(name = 'stl')", + "cc_library(name = 'malloc')", + "filegroup(name = 'toolchain', ", + " srcs = [':cc-compiler-local', ':cc-compiler-darwin', ':cc-compiler-piii',", + " ':cc-compiler-armeabi-v7a', ':empty'],", + ")", + "cc_toolchain(name = 'cc-compiler-k8', all_files = ':empty', compiler_files = ':empty',", + " cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ", + " linker_files = ':empty',", + " objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',", + ")", + "cc_toolchain(name = 'cc-compiler-piii', all_files = ':empty', compiler_files = ':empty',", + " cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ", + " linker_files = ':empty',", + " objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',", + ")", + "cc_toolchain(name = 'cc-compiler-darwin', all_files = ':empty', ", + " compiler_files = ':empty',", + " cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ", + " linker_files = ':empty',", + " objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',", + ")", + "cc_toolchain(name = 'cc-compiler-armeabi-v7a', all_files = ':empty', ", + " compiler_files = ':empty',", + " cpu = 'local', dwp_files = ':empty', dynamic_runtime_libs = [':empty'], ", + " linker_files = ':empty',", + " objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',", + ")"); + + config.create( + "/bazel_tools_workspace/tools/cpp/CROSSTOOL", + readFromResources("com/google/devtools/build/lib/MOCK_CROSSTOOL")); + } + + protected String getMockCrosstoolVersion() { + return "gcc-4.4.0-glibc-2.3.6"; + } + + protected String readCrosstoolFile() throws IOException { + return readFromResources("com/google/devtools/build/lib/MOCK_CROSSTOOL"); + } + + public static String readFromResources(String filename) throws IOException { + InputStream in = BazelMockCcSupport.class.getClassLoader().getResourceAsStream(filename); + return new String(ByteStreams.toByteArray(in), UTF_8); + } + + public String getMockCrosstoolPath() { + return "/bazel_tools_workspace/tools/cpp/"; + } + + public Predicate<String> labelNameFilter() { + return CC_LABEL_NAME_FILTER; + } +} diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/Crosstool.java b/src/test/java/com/google/devtools/build/lib/packages/util/Crosstool.java new file mode 100644 index 0000000000..455a43e60b --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/packages/util/Crosstool.java @@ -0,0 +1,189 @@ +// Copyright 2015 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.packages.util; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.devtools.build.lib.vfs.Path; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * A helper class to create a crosstool package containing a CROSSTOOL file, and the various + * rules needed for a mock - use this only for configured target tests, not for execution tests. + */ +final class Crosstool { + private static final ImmutableList<String> CROSSTOOL_BINARIES = + ImmutableList.of("compile", "dwp", "link", "objcopy"); + + private final MockToolsConfig config; + + private final String crosstoolTop; + private String version; + private String crosstoolFileContents; + private boolean addEmbeddedRuntimes; + private String staticRuntimesLabel; + private String dynamicRuntimesLabel; + private ImmutableList<String> archs; + private boolean addModuleMap; + private boolean supportsHeaderParsing; + + Crosstool(MockToolsConfig config, String crosstoolTop) { + this.config = config; + this.crosstoolTop = crosstoolTop; + } + + public Crosstool setAddModuleMap(boolean addModuleMap) { + this.addModuleMap = addModuleMap; + return this; + } + + public Crosstool setCrosstoolFile(String version, String crosstoolFileContents) { + this.version = version; + this.crosstoolFileContents = crosstoolFileContents; + return this; + } + + public Crosstool setSupportedArchs(ImmutableList<String> archs) { + this.archs = archs; + return this; + } + + public Crosstool setSupportsHeaderParsing(boolean supportsHeaderParsing) { + this.supportsHeaderParsing = supportsHeaderParsing; + return this; + } + + public Crosstool setEmbeddedRuntimes( + boolean addEmbeddedRuntimes, String staticRuntimesLabel, String dynamicRuntimesLabel) { + this.addEmbeddedRuntimes = addEmbeddedRuntimes; + this.staticRuntimesLabel = staticRuntimesLabel; + this.dynamicRuntimesLabel = dynamicRuntimesLabel; + return this; + } + + public void write() throws IOException { + String runtimes = ""; + for (String arch : archs) { + runtimes += + Joiner.on('\n') + .join( + "filegroup(name = 'dynamic-runtime-libs-" + arch + "',", + " licenses = ['unencumbered'],", + " srcs = ['libdynamic-runtime-lib-source.so'])", + "filegroup(name = 'static-runtime-libs-" + arch + "',", + " licenses = ['unencumbered'],", + " srcs = ['static-runtime-lib-source.a'])\n"); + } + + StringBuilder compilationTools = new StringBuilder(); + for (String compilationTool : CROSSTOOL_BINARIES) { + Collection<String> archTargets = new ArrayList<>(); + for (String arch : archs) { + archTargets.add(compilationTool + '-' + arch); + } + + compilationTools.append( + String.format( + "filegroup(name = '%s', srcs = ['%s'])\n", + compilationTool, + Joiner.on("', '").join(archTargets))); + for (String archTarget : archTargets) { + compilationTools.append( + String.format("filegroup(name = '%s', srcs = [':everything-multilib'])\n", archTarget)); + } + } + + List<String> compilerRules = Lists.newArrayList(); + + for (String arch : archs) { + String compilerRule; + String staticRuntimesString = + staticRuntimesLabel == null ? "" : ", '" + staticRuntimesLabel + "'"; + String dynamicRuntimesString = + dynamicRuntimesLabel == null ? "" : ", '" + dynamicRuntimesLabel + "'"; + + compilerRule = + Joiner.on("\n") + .join( + "cc_toolchain(", + " name = 'cc-compiler-" + arch + "',", + " output_licenses = ['unencumbered'],", + addModuleMap ? " module_map = 'crosstool.cppmap'," : "", + " cpu = '" + arch + "',", + " compiler_files = 'compile-" + arch + "',", + " dwp_files = 'dwp-" + arch + "',", + " linker_files = 'link-" + arch + "',", + " strip_files = ':every-file',", + " objcopy_files = 'objcopy-" + arch + "',", + " all_files = ':every-file',", + " licenses = ['unencumbered'],", + supportsHeaderParsing ? " supports_header_parsing = 1," : "", + " dynamic_runtime_libs = ['dynamic-runtime-libs-" + + arch + + "'" + + dynamicRuntimesString + + "],", + " static_runtime_libs = ['static-runtime-libs-" + + arch + + "'" + + staticRuntimesString + + "])"); + + compilationTools.append(compilerRule + "\n"); + compilerRules.add(":cc-compiler-" + arch); + } + + String build = + Joiner.on("\n") + .join( + "package(default_visibility=['//visibility:public'])", + "licenses(['restricted'])", + "", + "filegroup(name = 'everything-multilib',", + " srcs = glob(['" + version + "/**/*'],", + " exclude_directories = 1),", + " output_licenses = ['unencumbered'])", + "", + String.format( + "filegroup(name = 'everything', srcs = ['%s', ':every-file'])", + Joiner.on("', '").join(compilerRules)), + "", + String.format( + "filegroup(name = 'every-file', srcs = ['%s'%s%s])", + Joiner.on("', '").join(CROSSTOOL_BINARIES), + addEmbeddedRuntimes ? ", ':dynamic-runtime-libs-k8'" : "", + addEmbeddedRuntimes ? ", ':static-runtime-libs-k8'" : ""), + "", + compilationTools.toString(), + runtimes, + "", + // We add an empty :malloc target in case we need it. + "cc_library(name = 'malloc')"); + + config.create(crosstoolTop + "/" + version + "/x86/bin/gcc"); + config.create(crosstoolTop + "/" + version + "/x86/bin/ld"); + config.create(crosstoolTop + "/BUILD", build); + Path crosstoolPath = config.getPath(crosstoolTop + "/CROSSTOOL"); + if (crosstoolPath.exists()) { + crosstoolPath.delete(); + } + config.create(crosstoolTop + "/CROSSTOOL", crosstoolFileContents); + config.create(crosstoolTop + "/crosstool.cppmap", "module crosstool {}"); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java b/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java new file mode 100644 index 0000000000..5313c403e5 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java @@ -0,0 +1,298 @@ +// Copyright 2015 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.packages.util; + +import com.google.common.base.Predicate; +import com.google.common.base.Verify; +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.cmdline.LabelSyntaxException; +import com.google.devtools.build.lib.cmdline.PackageIdentifier; +import com.google.devtools.build.lib.cmdline.PackageIdentifier.RepositoryName; +import com.google.devtools.build.lib.testutil.TestConstants; +import com.google.devtools.build.lib.vfs.PathFragment; +import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig; +import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain; +import com.google.protobuf.TextFormat; + +import java.io.IOException; + +/** + * Creates mock BUILD files required for the C/C++ rules. + */ +public abstract class MockCcSupport { + + /** + * Filter to remove implicit crosstool artifact and module map inputs + * of C/C++ rules. + */ + public static final Predicate<Artifact> CC_ARTIFACT_FILTER = + new Predicate<Artifact>() { + @Override + public boolean apply(Artifact artifact) { + String basename = artifact.getExecPath().getBaseName(); + String pathString = artifact.getExecPathString(); + return !pathString.startsWith("third_party/crosstool/") + && !(pathString.contains("/internal/_middlemen") && basename.contains("crosstool")) + && !pathString.startsWith("_bin/build_interface_so") + && !pathString.endsWith(".cppmap"); + } + }; + + /** + * A feature configuration snippet useful for testing header processing. + */ + public static final String HEADER_PROCESSING_FEATURE_CONFIGURATION = + "" + + "feature {" + + " name: 'parse_headers'" + + " flag_set {" + + " action: 'c++-header-parsing'" + + " flag_group {" + + " flag: '<c++-header-parsing>'" + + " }" + + " }" + + "}" + + "feature {" + + " name: 'preprocess_headers'" + + " flag_set {" + + " action: 'c++-header-preprocessing'" + + " flag_group {" + + " flag: '<c++-header-preprocessing>'" + + " }" + + " }" + + "}"; + + /** + * A feature configuration snippet useful for testing the layering check. + */ + public static final String LAYERING_CHECK_FEATURE_CONFIGURATION = + "" + + "feature {" + + " name: 'layering_check'" + + " flag_set {" + + " action: 'c-compile'" + + " action: 'c++-compile'" + + " action: 'c++-header-parsing'" + + " action: 'c++-header-preprocessing'" + + " action: 'c++-module-compile'" + + " flag_group {" + + " flag: 'dependent_module_map_file:%{dependent_module_map_files}'" + + " }" + + " }" + + "}"; + + /** + * A feature configuration snippet useful for testing header modules. + */ + public static final String HEADER_MODULES_FEATURE_CONFIGURATION = + "" + + "feature {" + + " name: 'header_modules'" + + " implies: 'module_maps'" + + " implies: 'use_header_modules'" + + "}" + + "feature {" + + " name: 'module_maps'" + + " flag_set {" + + " action: 'c-compile'" + + " action: 'c++-compile'" + + " action: 'c++-header-parsing'" + + " action: 'c++-header-preprocessing'" + + " action: 'c++-module-compile'" + + " flag_group {" + + " flag: 'module_name:%{module_name}'" + + " flag: 'module_map_file:%{module_map_file}'" + + " }" + + " }" + + "}" + + "feature {" + + " name: 'use_header_modules'" + + " flag_set {" + + " action: 'c-compile'" + + " action: 'c++-compile'" + + " action: 'c++-header-parsing'" + + " action: 'c++-header-preprocessing'" + + " action: 'c++-modules-compile'" + + " flag_group {" + + " flag: 'module_file:%{module_files}'" + + " }" + + " }" + + "}"; + + public static final String THIN_LTO_CONFIGURATION = + "" + + "feature { " + + " name: 'thin_lto'" + + " flag_set {" + + " action: 'c-compile'" + + " action: 'c++-compile'" + + " flag_group {" + + " flag: '-Xclang-only=-Wno-inconsistent-missing-override'" + + " flag: '-flto'" + + " flag: '-O2'" + + " }" + + " }" + + "}"; + + /** Filter to remove implicit dependencies of C/C++ rules. */ + private final Predicate<Label> ccLabelFilter = + new Predicate<Label>() { + @Override + public boolean apply(Label label) { + return labelNameFilter().apply("//" + label.getPackageName()); + } + }; + + public static String mergeCrosstoolConfig(String original, CToolchain toolchain) + throws TextFormat.ParseException { + CrosstoolConfig.CrosstoolRelease.Builder builder = + CrosstoolConfig.CrosstoolRelease.newBuilder(); + TextFormat.merge(original, builder); + for (CToolchain.Builder toolchainBuilder : builder.getToolchainBuilderList()) { + toolchainBuilder.mergeFrom(toolchain); + } + return TextFormat.printToString(builder.build()); + } + + public abstract Predicate<String> labelNameFilter(); + + /** + * Setup the support for building C/C++. + */ + public abstract void setup(MockToolsConfig config) throws IOException; + + public void setupCrosstoolWithEmbeddedRuntimes(MockToolsConfig config) throws IOException { + createCrosstoolPackage(config, true); + } + + /** + * Creates a crosstool package by merging {@code toolchain} with the default mock CROSSTOOL file. + * + * @param partialToolchain A string representation of a CToolchain protocol buffer; note that + * this is allowed to be a partial buffer (required fields may be omitted). + */ + public void setupCrosstool(MockToolsConfig config, String partialToolchain) throws IOException { + CToolchain.Builder toolchainBuilder = CToolchain.newBuilder(); + TextFormat.merge(partialToolchain, toolchainBuilder); + setupCrosstool(config, toolchainBuilder.buildPartial()); + } + + /** + * Creates a crosstool package by merging {@code toolchain} with the default mock CROSSTOOL file. + */ + public void setupCrosstool(MockToolsConfig config, CToolchain toolchain) throws IOException { + createCrosstoolPackage( + config, /* addEmbeddedRuntimes= */ false, /* addModuleMap= */ true, null, null, toolchain); + } + + /** + * Create a crosstool package. For integration tests, it actually links in a working crosstool, + * for all other tests, it only creates a dummy package, with a working CROSSTOOL file. The code + * here matches the declarations in {@link CrosstoolTestHelper}. + * + * <p>If <code>addEmbeddedRuntimes</code> is true, it also adds filegroups for the embedded + * runtimes. + */ + public void setupCrosstool( + MockToolsConfig config, + boolean addEmbeddedRuntimes, + boolean addModuleMap, + String staticRuntimesLabel, + String dynamicRuntimesLabel, + CToolchain toolchain) + throws IOException { + createCrosstoolPackage( + config, + addEmbeddedRuntimes, + addModuleMap, + staticRuntimesLabel, + dynamicRuntimesLabel, + toolchain); + } + + protected static void createToolsCppPackage(MockToolsConfig config) throws IOException { + config.create( + "tools/cpp/BUILD", + "cc_library(name = 'stl')", + "filegroup(name='toolchain', srcs=['//third_party/crosstool'])"); + } + + protected void createCrosstoolPackage(MockToolsConfig config, boolean addEmbeddedRuntimes) + throws IOException { + createCrosstoolPackage(config, addEmbeddedRuntimes, /*addModuleMap=*/ true, null, null, null); + } + + protected String getCrosstoolTopPathForConfig(MockToolsConfig config) { + if (config.isRealFileSystem()) { + return getRealFilesystemCrosstoolTopPath(); + } else { + return getMockCrosstoolPath(); + } + } + + public abstract String getMockCrosstoolPath(); + + public static PackageIdentifier getMockCrosstoolsTop() { + try { + return PackageIdentifier.create( + RepositoryName.create(TestConstants.TOOLS_REPOSITORY), + new PathFragment(TestConstants.TOOLS_REPOSITORY_PATH)); + } catch (LabelSyntaxException e) { + Verify.verify(false); + throw new AssertionError(); + } + } + + protected void createCrosstoolPackage( + MockToolsConfig config, + boolean addEmbeddedRuntimes, + boolean addModuleMap, + String staticRuntimesLabel, + String dynamicRuntimesLabel, + CToolchain toolchain) + throws IOException { + String crosstoolTop = getCrosstoolTopPathForConfig(config); + if (config.isRealFileSystem()) { + config.linkTools(getRealFilesystemTools(crosstoolTop)); + } else { + String crosstoolFile = readCrosstoolFile(); + if (toolchain != null) { + crosstoolFile = mergeCrosstoolConfig(crosstoolFile, toolchain); + } + new Crosstool(config, crosstoolTop) + .setEmbeddedRuntimes(addEmbeddedRuntimes, staticRuntimesLabel, dynamicRuntimesLabel) + .setCrosstoolFile(getMockCrosstoolVersion(), crosstoolFile) + .setSupportedArchs(getCrosstoolArchs()) + .setAddModuleMap(addModuleMap) + .setSupportsHeaderParsing(true) + .write(); + } + } + + protected abstract String getMockCrosstoolVersion(); + + protected abstract String readCrosstoolFile() throws IOException; + + protected abstract ImmutableList<String> getCrosstoolArchs(); + + protected abstract String[] getRealFilesystemTools(String crosstoolTop); + + protected abstract String getRealFilesystemCrosstoolTopPath(); + + public final Predicate<Label> labelFilter() { + return ccLabelFilter; + } +} diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/MockToolsConfig.java b/src/test/java/com/google/devtools/build/lib/packages/util/MockToolsConfig.java index 038b5f121b..9580205148 100644 --- a/src/test/java/com/google/devtools/build/lib/packages/util/MockToolsConfig.java +++ b/src/test/java/com/google/devtools/build/lib/packages/util/MockToolsConfig.java @@ -79,8 +79,14 @@ public final class MockToolsConfig { } if (!newContent.toString().equals(existingContent)) { - throw new IOException("Conflict: '" + relativePath + "':\n'" + newContent + "'\n vs \n'" - + existingContent + "'"); + throw new IOException( + "Conflict: '" + + relativePath + + "':\n'" + + newContent + + "'\n vs \n'" + + existingContent + + "'"); } } return path; diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationHelper.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationHelper.java index f28af6dc0f..fa18959eea 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationHelper.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationHelper.java @@ -14,7 +14,7 @@ package com.google.devtools.build.lib.rules.cpp; -import com.google.devtools.build.lib.testutil.TestConstants; +import com.google.devtools.build.lib.analysis.util.AnalysisMock; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig; @@ -29,7 +29,8 @@ import java.io.IOException; */ public class CrosstoolConfigurationHelper { public static Path overwriteCrosstoolFile(Path workspace, String content) throws IOException { - Path crosstool = workspace.getRelative(TestConstants.MOCK_CROSSTOOL_PATH + "/CROSSTOOL"); + Path crosstool = + workspace.getRelative(AnalysisMock.get().ccSupport().getMockCrosstoolPath() + "/CROSSTOOL"); long newMTime = crosstool.exists() ? crosstool.getLastModifiedTime() + 1 : -1; crosstool.delete(); FileSystemUtils.createDirectoryAndParents(crosstool.getParentDirectory()); diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoaderTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoaderTest.java index fa0cc94f7b..9a1a7468ec 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoaderTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoaderTest.java @@ -31,8 +31,10 @@ import com.google.devtools.build.lib.analysis.config.ConfigurationEnvironment; import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.analysis.util.AnalysisMock; import com.google.devtools.build.lib.analysis.util.AnalysisTestCase; +import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.cmdline.PackageIdentifier; +import com.google.devtools.build.lib.packages.util.MockCcSupport; import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool; import com.google.devtools.build.lib.testutil.TestConstants; import com.google.devtools.build.lib.vfs.PathFragment; @@ -670,7 +672,7 @@ public class CrosstoolConfigurationLoaderTest extends AnalysisTestCase { PackageIdentifier.create( TestConstants.TOOLS_REPOSITORY, new PathFragment( - new PathFragment(TestConstants.MOCK_CROSSTOOL_PATH), new PathFragment(path))); + new PathFragment(TestConstants.TOOLS_REPOSITORY_PATH), new PathFragment(path))); return packageIdentifier.getPathFragment(); } @@ -989,17 +991,17 @@ public class CrosstoolConfigurationLoaderTest extends AnalysisTestCase { + " dynamic_runtimes_filegroup: \"dynamic-group\"" + "}\n"); - final String ctTop = TestConstants.TOOLS_REPOSITORY + "//" + TestConstants.MOCK_CROSSTOOL_PATH; + final PackageIdentifier ctTop = MockCcSupport.getMockCrosstoolsTop(); CppConfiguration defaultLibs = create(loader, "--cpu=piii"); assertEquals( - ctTop + ":static-runtime-libs-piii", defaultLibs.getStaticRuntimeLibsLabel().toString()); + Label.create(ctTop, "static-runtime-libs-piii"), defaultLibs.getStaticRuntimeLibsLabel()); assertEquals( - ctTop + ":dynamic-runtime-libs-piii", defaultLibs.getDynamicRuntimeLibsLabel().toString()); + Label.create(ctTop, "dynamic-runtime-libs-piii"), defaultLibs.getDynamicRuntimeLibsLabel()); CppConfiguration customLibs = create(loader, "--cpu=k8"); - assertEquals(ctTop + ":static-group", customLibs.getStaticRuntimeLibsLabel().toString()); - assertEquals(ctTop + ":dynamic-group", customLibs.getDynamicRuntimeLibsLabel().toString()); + assertEquals(Label.create(ctTop, "static-group"), customLibs.getStaticRuntimeLibsLabel()); + assertEquals(Label.create(ctTop, "dynamic-group"), customLibs.getDynamicRuntimeLibsLabel()); } /* diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/LibraryLinkingTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/LibraryLinkingTest.java new file mode 100644 index 0000000000..9104187e76 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/LibraryLinkingTest.java @@ -0,0 +1,123 @@ +// Copyright 2015 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.rules.cpp; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.devtools.build.lib.Constants; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.FileProvider; +import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.List; + +/** + * Test for shared library linking {@link CppLinkAction}. + */ +@RunWith(JUnit4.class) +public final class LibraryLinkingTest extends BuildViewTestCase { + private List<String> getLinkOpts(CppLinkAction linkAction, String... optionPatterns) + throws Exception { + // Strip the first parameter from the argv, which is the gcc command. + return linkAction.getRawLinkArgv().subList(1, optionPatterns.length + 3); + } + + private void assertLinkopts(CppLinkAction linkAction, String... optionPatterns) throws Exception { + List<String> linkopts = getLinkOpts(linkAction, optionPatterns); + for (int i = 0; i < optionPatterns.length; i++) { + assertThat(linkopts.get(i)).matches(optionPatterns[i]); + } + } + + @Test + public void testGeneratedLib() throws Exception { + ConfiguredTarget genlib = + scratchConfiguredTarget( + "genrule", + "thebinary.so", + "genrule(name = 'genlib',", + " outs = ['genlib.a'],", + " cmd = '')", + "cc_library(name = 'thelib',", + " srcs = [':genlib'],", + " linkstatic = 1)", + "cc_binary(name = 'thebinary.so',", + " deps = [':thelib'],", + " linkstatic = 1,", + " linkshared = 1)"); + Artifact executable = getExecutable(genlib); + CppLinkAction linkAction = (CppLinkAction) getGeneratingAction(executable); + assertLinkopts( + linkAction, + "-shared", + "-o", + Constants.PRODUCT_NAME + "-out/.+/genrule/thebinary.so", + "-Wl,-whole-archive", + Constants.PRODUCT_NAME + "-out/.+/genrule/genlib.a", + "-Wl,-no-whole-archive"); + } + + /** + * Tests that the shared library version of a cc_library includes linkopts settings + * in its link command line, but the archive library version doesn't. + */ + @Test + public void testCcLibraryLinkopts() throws Exception { + scratch.overwriteFile( + "custom_malloc/BUILD", + "cc_library(name = 'custom_malloc',", + " srcs = ['custom_malloc.cc'],", + " linkopts = ['-Lmalloc_dir -lmalloc_opt']);"); + + ConfiguredTarget ccLib = getConfiguredTarget("//custom_malloc:custom_malloc"); + final String linkOpt1 = "-Lmalloc_dir"; + final String linkOpt2 = "-lmalloc_opt"; + + // Archive library version: + Artifact archiveLib = + Iterables.getOnlyElement( + Iterables.filter( + ccLib.getProvider(FileProvider.class).getFilesToBuild(), + new Predicate<Artifact>() { + @Override + public boolean apply(Artifact artifact) { + return artifact.getFilename().equals("libcustom_malloc.a"); + } + })); + CppLinkAction archiveLink = (CppLinkAction) getGeneratingAction(archiveLib); + List<String> args = archiveLink.getArgv(); + assertThat(args).doesNotContain(linkOpt1); + assertThat(args).doesNotContain(linkOpt2); + + // Shared library version: + Artifact soLib = + Iterables.getOnlyElement( + ccLib + .getProvider(CcExecutionDynamicLibrariesProvider.class) + .getExecutionDynamicLibraryArtifacts()); + // This artifact is generated by a SolibSymlinkAction, so we need to go back two levels. + CppLinkAction solibLink = + (CppLinkAction) getGeneratingAction(getGeneratingAction(soLib).getPrimaryInput()); + args = solibLink.getArgv(); + assertThat(args).contains(linkOpt1); + assertThat(args).contains(linkOpt2); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java index b6d3e54ee2..a2c073b67b 100644 --- a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java +++ b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java @@ -69,5 +69,5 @@ public class TestConstants { public static final String TOOLS_REPOSITORY = "@bazel_tools"; - public static final String MOCK_CROSSTOOL_PATH = "tools/cpp"; + public static final String TOOLS_REPOSITORY_PATH = "tools/cpp"; } |