diff options
author | Yun Peng <pcloudy@google.com> | 2017-09-15 15:59:14 +0200 |
---|---|---|
committer | László Csomor <laszlocsomor@google.com> | 2017-09-18 11:25:37 +0200 |
commit | 394211bf88b88c25036429e99894fa7c60eaaace (patch) | |
tree | a5a99354a9f3b8530bb0d98d61d8844b21348ecd | |
parent | f8480fac37692083b2f9c4e69b9b9e9e2c9b1853 (diff) |
Bazel now can build dynamic library from cc_library
Working towards: https://github.com/bazelbuild/bazel/issues/3311
When building dynamic library on Windows, Bazel builds an import library
and a DLL.
Bazel provides a feature called windows_export_all_symbols, if this
feature is enabled(and no_windows_export_all_symbols is not) for a
cc_library, then Bazel parses object files of that cc_library to generate
a DEF file that will be used during linking time to export symbols from
DLL. This feature can be specified at crosstool, package, target and
command line level.
A few differences from Unix platforms:
1. We don't build the shared library on Windows by default, users have to
specifiy --output_groups=dynamic_library for building dynamic libraries.
This output group is also available on other platforms.
2. By default, cc_test is dynamically linked on Unix, but it will be
statically linked on Windows by default. (meaning the default value of
linkstatic in cc_test is 1 on Windows, and 0 on other platforms)
3. For global data symbols, __declspec(dllimport) must still be used in
source files.
Remaining issues:
1. Extensions for import library and DLL are not correct yet.
2. DLLs are not guaranteed to be available during runtime yet.
3. Diamond problem
If a cc_library A is specified as linkstatic=0, then no dynamic library
will be built for it, so if another cc_library B depends on it, A will
be statically linked into B, and if a cc_binary C depends on B, A will
also be statically linked into C and B will be dynamically linked to C.
This is wrong because A is duplicated in both B and C.
It is essentially a diamond problem describled in C++ Transitive Library.
(https://docs.google.com/document/d/1-tv0_79zGyBoDmaP_pYWaBVUwHUteLpAs90_rUl-VY8/edit?usp=sharing)
Hopefully, we can avoid this by using cc_shared_library rule in future.
Change-Id: I23640d4caf8afe65d60b1522af6368536d7a8408
PiperOrigin-RevId: 168829958
23 files changed, 640 insertions, 120 deletions
@@ -161,6 +161,7 @@ py_binary( "//src/java_tools/junitrunner/java/com/google/testing/junit/runner:Runner_deploy.jar", "//src/java_tools/junitrunner/java/com/google/testing/junit/runner:ExperimentalRunner_deploy.jar", "//src/java_tools/junitrunner/java/com/google/testing/coverage:embedded_tools", + "//third_party/def_parser:srcs", "//third_party/ijar", "//third_party/ijar:embedded_tools", "//src/java_tools/buildjar/java/com/google/devtools/build/java/turbine:turbine_deploy.jar", @@ -185,14 +186,17 @@ py_binary( ":windows": [ "//src/java_tools/singlejar:SingleJar_deploy.jar", "//src/tools/launcher:launcher", + "//third_party/def_parser:def_parser", ], ":windows_msys": [ "//src/java_tools/singlejar:SingleJar_deploy.jar", "//src/tools/launcher:launcher", + "//third_party/def_parser:def_parser", ], ":windows_msvc": [ "//src/java_tools/singlejar:SingleJar_deploy.jar", "//src/tools/launcher:launcher", + "//third_party/def_parser:def_parser", ], ":arm": [ "//src/java_tools/singlejar:SingleJar_deploy.jar", diff --git a/src/create_embedded_tools.py b/src/create_embedded_tools.py index 699edd09f5..17d747fafc 100644 --- a/src/create_embedded_tools.py +++ b/src/create_embedded_tools.py @@ -45,6 +45,7 @@ output_paths = [ ('*Runner_deploy.jar', lambda x: 'tools/jdk/TestRunner_deploy.jar'), ('*singlejar', lambda x: 'tools/jdk/singlejar/singlejar'), ('*launcher.exe', lambda x: 'tools/launcher/launcher.exe'), + ('*def_parser.exe', lambda x: 'tools/def_parser/def_parser.exe'), ('*ijar.exe', lambda x: 'tools/jdk/ijar/ijar.exe'), ('*ijar', lambda x: 'tools/jdk/ijar/ijar'), ('*zipper.exe', lambda x: 'tools/zip/zipper/zipper.exe'), diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java index 922d542d35..21a5a31ffd 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java @@ -355,37 +355,51 @@ public class BazelRuleClassProvider { } }; - public static final RuleSet CPP_RULES = - new RuleSet() { - @Override - public void init(Builder builder) { - builder.addConfig( - CppOptions.class, new CppConfigurationLoader(Functions.<String>identity())); - - builder.addBuildInfoFactory(new CppBuildInfo()); - builder.addDynamicTransitionMaps(CppRuleClasses.DYNAMIC_TRANSITIONS_MAP); - - builder.addRuleDefinition(new CcToolchainRule()); - builder.addRuleDefinition(new CcToolchainSuiteRule()); - builder.addRuleDefinition(new CcToolchainAlias.CcToolchainAliasRule()); - builder.addRuleDefinition(new CcIncLibraryRule()); - builder.addRuleDefinition(new BazelCppRuleClasses.CcLinkingRule()); - builder.addRuleDefinition(new BazelCppRuleClasses.CcDeclRule()); - builder.addRuleDefinition(new BazelCppRuleClasses.CcBaseRule()); - builder.addRuleDefinition(new BazelCppRuleClasses.CcRule()); - builder.addRuleDefinition(new BazelCppRuleClasses.CcBinaryBaseRule()); - builder.addRuleDefinition(new BazelCcBinaryRule()); - builder.addRuleDefinition(new BazelCcTestRule()); - builder.addRuleDefinition(new BazelCppRuleClasses.CcLibraryBaseRule()); - builder.addRuleDefinition(new BazelCcLibraryRule()); - builder.addRuleDefinition(new BazelCcIncLibraryRule()); - } + /** + * Set the label for Windows DEF parser. In bazel, it should be + * @bazel_tools//tools/def_parser:def_parser, otherwise it should be null. + * + * <p>TODO(pcloudy): Remove this after Bazel rule definitions are not used internally anymore. + * Related bug b/63658220 + */ + public static final RuleSet CPP_RULES = cppRules("@bazel_tools//tools/def_parser:def_parser"); + + public static RuleSet cppRules() { + return cppRules(null); + } - @Override - public ImmutableList<RuleSet> requires() { - return ImmutableList.of(CoreRules.INSTANCE, PLATFORM_RULES); - } - }; + public static RuleSet cppRules(String defParserLabel) { + return new RuleSet() { + @Override + public void init(Builder builder) { + builder.addConfig( + CppOptions.class, new CppConfigurationLoader(Functions.<String>identity())); + + builder.addBuildInfoFactory(new CppBuildInfo()); + builder.addDynamicTransitionMaps(CppRuleClasses.DYNAMIC_TRANSITIONS_MAP); + + builder.addRuleDefinition(new CcToolchainRule(defParserLabel)); + builder.addRuleDefinition(new CcToolchainSuiteRule()); + builder.addRuleDefinition(new CcToolchainAlias.CcToolchainAliasRule()); + builder.addRuleDefinition(new CcIncLibraryRule()); + builder.addRuleDefinition(new BazelCppRuleClasses.CcLinkingRule()); + builder.addRuleDefinition(new BazelCppRuleClasses.CcDeclRule()); + builder.addRuleDefinition(new BazelCppRuleClasses.CcBaseRule()); + builder.addRuleDefinition(new BazelCppRuleClasses.CcRule()); + builder.addRuleDefinition(new BazelCppRuleClasses.CcBinaryBaseRule()); + builder.addRuleDefinition(new BazelCcBinaryRule()); + builder.addRuleDefinition(new BazelCcTestRule()); + builder.addRuleDefinition(new BazelCppRuleClasses.CcLibraryBaseRule()); + builder.addRuleDefinition(new BazelCcLibraryRule()); + builder.addRuleDefinition(new BazelCcIncLibraryRule()); + } + + @Override + public ImmutableList<RuleSet> requires() { + return ImmutableList.of(CoreRules.INSTANCE, PLATFORM_RULES); + } + }; + } public static final RuleSet CPP_PROTO_RULES = new RuleSet() { diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcTestRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcTestRule.java index 72e9517572..292bd9c38b 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcTestRule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcTestRule.java @@ -29,6 +29,7 @@ import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; import com.google.devtools.build.lib.packages.TriState; import com.google.devtools.build.lib.rules.cpp.CppConfiguration; import com.google.devtools.build.lib.rules.cpp.CppRuleClasses; +import com.google.devtools.build.lib.util.OS; /** Rule definition for cc_test rules. */ public final class BazelCcTestRule implements RuleDefinition { @@ -37,7 +38,11 @@ public final class BazelCcTestRule implements RuleDefinition { return builder .requiresConfigurationFragments(CppConfiguration.class) .setImplicitOutputsFunction(CppRuleClasses.CC_BINARY_DEBUG_PACKAGE) - .override(attr("linkstatic", BOOLEAN).value(false)) + // We don't want C++ tests to be dynamically linked by default on Windows, + // because windows_export_all_symbols is not enabled by default, and it cannot solve + // all symbols visibility issues, for example, users still have to use __declspec(dllimport) + // to decorate data symbols imported from DLL. + .override(attr("linkstatic", BOOLEAN).value(OS.getCurrent() == OS.WINDOWS)) .override(attr("stamp", TRISTATE).value(TriState.NO)) // Oh weary adventurer, endeavour not to remove this attribute, enticing as that may be, // given that no code referenceth it. Should ye be on the verge of yielding to the diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java index b846dfaef3..e90dda7c54 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java @@ -187,11 +187,17 @@ public abstract class CcLibrary implements RuleConfiguredTargetFactory { // doesn't support it, then register an action which complains when triggered, // which only happens when some rule explicitly depends on the dynamic library. if (!createDynamicLibrary && !supportsDynamicLinker) { - Artifact solibArtifact = + ImmutableList.Builder<Artifact> dynamicLibraries = ImmutableList.builder(); + dynamicLibraries.add( + CppHelper.getLinuxLinkedArtifact( + ruleContext, ruleContext.getConfiguration(), LinkTargetType.DYNAMIC_LIBRARY)); + if (ccToolchain.getCppConfiguration().useInterfaceSharedObjects()) { + dynamicLibraries.add( CppHelper.getLinuxLinkedArtifact( - ruleContext, ruleContext.getConfiguration(), LinkTargetType.DYNAMIC_LIBRARY); + ruleContext, ruleContext.getConfiguration(), LinkTargetType.INTERFACE_DYNAMIC_LIBRARY)); + } ruleContext.registerAction(new FailAction(ruleContext.getActionOwner(), - ImmutableList.of(solibArtifact), "Toolchain does not support dynamic linking")); + dynamicLibraries.build(), "Toolchain does not support dynamic linking")); } else if (!createDynamicLibrary && ruleContext.attributes().isConfigurable("srcs")) { // If "srcs" is configurable, the .so output is always declared because the logic that @@ -199,11 +205,17 @@ public abstract class CcLibrary implements RuleConfiguredTargetFactory { // Here, where we *do* have the correct value, it may not contain any source files to // generate an .so with. If that's the case, register a fake generating action to prevent // a "no generating action for this artifact" error. - Artifact solibArtifact = + ImmutableList.Builder<Artifact> dynamicLibraries = ImmutableList.builder(); + dynamicLibraries.add( + CppHelper.getLinuxLinkedArtifact( + ruleContext, ruleContext.getConfiguration(), LinkTargetType.DYNAMIC_LIBRARY)); + if (ccToolchain.getCppConfiguration().useInterfaceSharedObjects()) { + dynamicLibraries.add( CppHelper.getLinuxLinkedArtifact( - ruleContext, ruleContext.getConfiguration(), LinkTargetType.DYNAMIC_LIBRARY); + ruleContext, ruleContext.getConfiguration(), LinkTargetType.INTERFACE_DYNAMIC_LIBRARY)); + } ruleContext.registerAction(new FailAction(ruleContext.getActionOwner(), - ImmutableList.of(solibArtifact), "configurable \"srcs\" triggers an implicit .so output " + dynamicLibraries.build(), "configurable \"srcs\" triggers an implicit .so output " + "even though there are no sources to compile in this configuration")); } @@ -258,9 +270,12 @@ public abstract class CcLibrary implements RuleConfiguredTargetFactory { NestedSetBuilder<Artifact> filesBuilder = NestedSetBuilder.stableOrder(); filesBuilder.addAll(LinkerInputs.toLibraryArtifacts(linkedLibraries.getStaticLibraries())); filesBuilder.addAll(LinkerInputs.toLibraryArtifacts(linkedLibraries.getPicStaticLibraries())); - filesBuilder.addAll(LinkerInputs.toNonSolibArtifacts(linkedLibraries.getDynamicLibraries())); - filesBuilder.addAll( - LinkerInputs.toNonSolibArtifacts(linkedLibraries.getExecutionDynamicLibraries())); + + if (!featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { + filesBuilder.addAll(LinkerInputs.toNonSolibArtifacts(linkedLibraries.getDynamicLibraries())); + filesBuilder.addAll( + LinkerInputs.toNonSolibArtifacts(linkedLibraries.getExecutionDynamicLibraries())); + } CcLinkingOutputs linkingOutputs = info.getCcLinkingOutputs(); if (!featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULE_CODEGEN)) { diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java index 02c7c26002..58d3f603a0 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java @@ -89,6 +89,12 @@ public final class CcLibraryHelper { + "hidden_header_tokens" + OutputGroupProvider.INTERNAL_SUFFIX; + /** A string constant for the name of archive library(.a, .lo) output group. */ + public static final String ARCHIVE_LIBRARY_OUTPUT_GROUP_NAME = "cc_archive"; + + /** A string constant for the name of dynamic library output group. */ + public static final String DYNAMIC_LIBRARY_OUTPUT_GROUP_NAME = "cc_dynamic_library"; + /** * A group of source file types and action names for builds controlled by CcLibraryHelper. * Determines what file types CcLibraryHelper considers sources and what action configs are @@ -1137,10 +1143,20 @@ public final class CcLibraryHelper { configuration, Link.LinkTargetType.DYNAMIC_LIBRARY, linkedArtifactNameSuffix)); + + if (ccToolchain.getCppConfiguration().useInterfaceSharedObjects() + && emitInterfaceSharedObjects) { + dynamicLibrary.add( + CppHelper.getLinuxLinkedArtifact( + ruleContext, + configuration, + LinkTargetType.INTERFACE_DYNAMIC_LIBRARY, + linkedArtifactNameSuffix)); + } } - outputGroups.put("archive", archiveFile.build()); - outputGroups.put("dynamic_library", dynamicLibrary.build()); + outputGroups.put(ARCHIVE_LIBRARY_OUTPUT_GROUP_NAME, archiveFile.build()); + outputGroups.put(DYNAMIC_LIBRARY_OUTPUT_GROUP_NAME, dynamicLibrary.build()); } /** diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java index 48afc4455f..e44b32b7ff 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java @@ -392,6 +392,9 @@ public class CcToolchain implements RuleConfiguredTargetFactory { cppConfiguration.supportsInterfaceSharedObjects() ? ruleContext.getPrerequisiteArtifact("$link_dynamic_library_tool", Mode.HOST) : null, + ruleContext.attributes().has("$def_parser") + ? ruleContext.getPrerequisiteArtifact("$def_parser", Mode.HOST) + : null, getEnvironment(ruleContext), builtInIncludeDirectories, sysroot); diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java index 8f85034126..b79164bbeb 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java @@ -63,6 +63,7 @@ public final class CcToolchainProvider extends ToolchainInfo { ImmutableList.<Artifact>of(), NestedSetBuilder.<Pair<String, String>>emptySet(Order.COMPILE_ORDER), null, + null, ImmutableMap.<String, String>of(), ImmutableList.<PathFragment>of(), null); @@ -90,6 +91,7 @@ public final class CcToolchainProvider extends ToolchainInfo { private final ImmutableList<Artifact> builtinIncludeFiles; private final NestedSet<Pair<String, String>> coverageEnvironment; @Nullable private final Artifact linkDynamicLibraryTool; + @Nullable private final Artifact defParser; private final ImmutableMap<String, String> environment; private final ImmutableList<PathFragment> builtInIncludeDirectories; @Nullable private final PathFragment sysroot; @@ -118,6 +120,7 @@ public final class CcToolchainProvider extends ToolchainInfo { ImmutableList<Artifact> builtinIncludeFiles, NestedSet<Pair<String, String>> coverageEnvironment, Artifact linkDynamicLibraryTool, + Artifact defParser, ImmutableMap<String, String> environment, ImmutableList<PathFragment> builtInIncludeDirectories, @Nullable PathFragment sysroot) { @@ -145,6 +148,7 @@ public final class CcToolchainProvider extends ToolchainInfo { this.builtinIncludeFiles = builtinIncludeFiles; this.coverageEnvironment = coverageEnvironment; this.linkDynamicLibraryTool = linkDynamicLibraryTool; + this.defParser = defParser; this.environment = environment; this.builtInIncludeDirectories = builtInIncludeDirectories; this.sysroot = sysroot; @@ -330,6 +334,14 @@ public final class CcToolchainProvider extends ToolchainInfo { } /** + * Returns the tool which should be used to parser object files for generating DEF file on + * Windows. The label of this tool is //third_party/def_parser:def_parser. + */ + public Artifact getDefParserTool() { + return defParser; + } + + /** * Returns the tool that builds interface libraries from dynamic libraries. */ public Artifact getInterfaceSoBuilder() { diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java index c691d434b3..5f9f07a4e3 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java @@ -41,6 +41,23 @@ import com.google.devtools.build.lib.rules.cpp.CppRuleClasses.LipoTransition; public final class CcToolchainRule implements RuleDefinition { /** + * The label points to the Windows object file parser. In bazel, it should be + * //tools/def_parser:def_parser, otherwise it should be null. + * + * <p>TODO(pcloudy): Remove this after Bazel rule definitions are not used internally anymore. + * Related bug b/63658220 + */ + private final String defParserLabel; + + public CcToolchainRule(String defParser) { + this.defParserLabel = defParser; + } + + public CcToolchainRule() { + this.defParserLabel = null; + } + + /** * Determines if the given target is a cc_toolchain or one of its subclasses. New subclasses * should be added to this method. */ @@ -60,6 +77,13 @@ public final class CcToolchainRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { final Label zipper = env.getToolsLabel("//tools/zip:zipper"); + if (defParserLabel != null) { + builder.add( + attr("$def_parser", LABEL) + .cfg(HOST) + .singleArtifact() + .value(env.getLabel(defParserLabel))); + } return builder .setUndocumented() .requiresConfigurationFragments(CppConfiguration.class) diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java index 2d47b74687..7a21660d3c 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java @@ -79,6 +79,8 @@ public class CppLinkActionBuilder { */ public static final String THINLTO_PARAM_FILE_VARIABLE = "thinlto_param_file"; + public static final String DEF_FILE_PATH_VARIABLE = "def_file_path"; + /** * A build variable to let thinlto know where it should write linker flags when indexing. */ @@ -190,6 +192,7 @@ public class CppLinkActionBuilder { private LinkStaticness linkStaticness = LinkStaticness.FULLY_STATIC; private String libraryIdentifier = null; private ImmutableMap<Artifact, Artifact> ltoBitcodeFiles; + private Artifact defFile; private boolean fake; private boolean isNativeDeps; @@ -561,10 +564,13 @@ public class CppLinkActionBuilder { } switch (linkType) { - // We currently can't split dynamic library links if they have interface outputs. That was - // probably an unintended side effect of the change that introduced interface outputs. + // On Unix, we currently can't split dynamic library links if they have interface outputs. + // That was probably an unintended side effect of the change that introduced interface + // outputs. + // On Windows, We can always split the command line when building DLL. case DYNAMIC_LIBRARY: - return interfaceOutput == null; + return (interfaceOutput == null + || featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)); case EXECUTABLE: case STATIC_LIBRARY: case PIC_STATIC_LIBRARY: @@ -819,6 +825,9 @@ public class CppLinkActionBuilder { dependencyInputsBuilder.addAll(linkstamps); dependencyInputsBuilder.addTransitive(compilationInputs.build()); } + if (defFile != null) { + dependencyInputsBuilder.add(defFile); + } Iterable<Artifact> expandedInputs = LinkerInputs.toLibraryArtifacts( @@ -1095,6 +1104,11 @@ public class CppLinkActionBuilder { return this; } + public CppLinkActionBuilder setDefFile(Artifact defFile) { + this.defFile = defFile; + return this; + } + /** * Adds a single object file to the set of inputs. */ @@ -1533,6 +1547,10 @@ public class CppLinkActionBuilder { INTERFACE_LIBRARY_OUTPUT_VARIABLE, shouldGenerateInterfaceLibrary ? interfaceLibraryOutput.getExecPathString() : "ignored"); + if (defFile != null) { + buildVariables.addStringVariable(DEF_FILE_PATH_VARIABLE, defFile.getExecPathString()); + } + // Variables arising from the toolchain buildVariables .addAllStringVariables(toolchain.getBuildVariables()) diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java index 72b65a983f..2fcd33209b 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java @@ -14,6 +14,8 @@ package com.google.devtools.build.lib.rules.cpp; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; @@ -23,10 +25,14 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.FailAction; +import com.google.devtools.build.lib.actions.ParameterFile; import com.google.devtools.build.lib.analysis.AnalysisEnvironment; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; +import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; +import com.google.devtools.build.lib.analysis.actions.ParameterFileWriteAction; +import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.PerLabelOptions; import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector; @@ -54,6 +60,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import javax.annotation.Nullable; /** * Representation of a C/C++ compilation. Its purpose is to share the code that creates compilation @@ -681,6 +688,60 @@ public final class CppModel { } /** + * Create actions for parsing object files to generate a DEF file, should on be used on Windows. + * + * <p>The method only creates the actions when WINDOWS_EXPORT_ALL_SYMBOLS feature is enabled and + * NO_WINDOWS_EXPORT_ALL_SYMBOLS feature is not enabled. + * + * @param objectFiles A list of object files to parse + * @param dllName The DLL name to be written into the DEF file, it specifies which DLL is required + * at runtime + * @return The DEF file artifact, null if actions are not created. + */ + @Nullable + public Artifact createDefFileActions(ImmutableList<Artifact> objectFiles, String dllName) { + if (!featureConfiguration.isEnabled(CppRuleClasses.WINDOWS_EXPORT_ALL_SYMBOLS) + || featureConfiguration.isEnabled(CppRuleClasses.NO_WINDOWS_EXPORT_ALL_SYMBOLS)) { + return null; + } + Artifact defFile = ruleContext.getBinArtifact(ruleContext.getLabel().getName() + ".def"); + CustomCommandLine.Builder argv = new CustomCommandLine.Builder(); + for (Artifact objectFile : objectFiles) { + argv.addDynamicString(objectFile.getExecPathString()); + } + + Artifact paramFile = + ruleContext.getDerivedArtifact( + ParameterFile.derivePath(defFile.getRootRelativePath()), defFile.getRoot()); + + ruleContext.registerAction( + new ParameterFileWriteAction( + ruleContext.getActionOwner(), + paramFile, + argv.build(), + ParameterFile.ParameterFileType.SHELL_QUOTED, + UTF_8)); + + Artifact defParser = ccToolchain.getDefParserTool(); + ruleContext.registerAction( + new SpawnAction.Builder() + .addInput(paramFile) + .addInputs(objectFiles) + .addOutput(defFile) + .setExecutable(defParser) + .useDefaultShellEnvironment() + .addCommandLine( + CustomCommandLine.builder() + .addExecPath(defFile) + .addDynamicString(dllName) + .addPrefixedExecPath("@", paramFile) + .build()) + .setMnemonic("DefParser") + .build(ruleContext)); + return defFile; + } + + /** * Constructs the C++ compiler actions. It generally creates one action for every specified source * file. It takes into account LIPO, fake-ness, coverage, and PIC, in addition to using the * settings specified on the current object. This method should only be called once. @@ -1417,11 +1478,14 @@ public final class CppModel { configuration, LinkTargetType.INTERFACE_DYNAMIC_LIBRARY, linkedArtifactNameSuffix); - sonameLinkopts = - ImmutableList.of( - "-Wl,-soname=" - + SolibSymlinkAction.getDynamicLibrarySoname( - soImpl.getRootRelativePath(), /* preserveName= */ false)); + // TODO(b/28946988): Remove this hard-coded flag. + if (!featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { + sonameLinkopts = + ImmutableList.of( + "-Wl,-soname=" + + SolibSymlinkAction.getDynamicLibrarySoname( + soImpl.getRootRelativePath(), /* preserveName= */ false)); + } } CppLinkActionBuilder dynamicLinkActionBuilder = @@ -1442,6 +1506,24 @@ public final class CppModel { ccToolchain.getDynamicRuntimeLinkInputs()) .addVariablesExtensions(variablesExtensions); + Artifact defFile = + createDefFileActions( + ccOutputs.getObjectFiles(false), + SolibSymlinkAction.getDynamicLibrarySoname(soImpl.getRootRelativePath(), true)); + if (defFile != null) { + dynamicLinkActionBuilder.setDefFile(defFile); + } + + // On Windows, we cannot build a shared library with symbols unresolved, so here we dynamically + // link to all it's dependencies. + if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { + CcLinkParams.Builder ccLinkParamsbuilder = + CcLinkParams.builder(/* linkingStatically= */ false, /* linkShared= */ true); + ccLinkParamsbuilder.addCcLibrary( + ruleContext, false, ImmutableList.of(), CcLinkingOutputs.EMPTY); + dynamicLinkActionBuilder.addLinkParams(ccLinkParamsbuilder.build(), ruleContext); + } + if (!ccOutputs.getLtoBitcodeFiles().isEmpty() && featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) { dynamicLinkActionBuilder.setLtoIndexing(true); diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java index 275c717353..4e8139e096 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java @@ -311,6 +311,22 @@ public class CppRuleClasses { public static final String GENERATE_PDB_FILE = "generate_pdb_file"; /** + * A string constant for a feature that automatically exporting symbols on Windows. Bazel + * generates a DEF file for object files of a cc_library, then use it at linking time. This + * feature should only be used for toolchains targeting Windows, and the toolchain should support + * using DEF files for exporting symbols. + */ + public static final String WINDOWS_EXPORT_ALL_SYMBOLS = "windows_export_all_symbols"; + + /** A string constant for a feature to disable WINDOWS_EXPORT_ALL_SYMBOLS. */ + public static final String NO_WINDOWS_EXPORT_ALL_SYMBOLS = "no_windows_export_all_symbols"; + + /** + * A string constant for a feature that indicates we are using a toolchain building for Windows. + */ + public static final String TARGETS_WINDOWS = "targets_windows"; + + /** * A string constant for no_stripping feature, if it's specified, then no strip action config is * needed, instead the stripped binary will simply be a symlink (or a copy on Windows) of the * original binary. 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 f3525b3e4c..ca994963a8 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 @@ -165,6 +165,11 @@ public final class BazelAnalysisMock extends AnalysisMock { "cc_binary(name='launcher', srcs=['launcher_main.cc'])"); config.create( + "/bazel_tools_workspace/tools/def_parser/BUILD", + "package(default_visibility=['//visibility:public'])", + "filegroup(name='def_parser', srcs=['def_parser.exe'])"); + + config.create( "/bazel_tools_workspace/objcproto/BUILD", "package(default_visibility=['//visibility:public'])", "objc_library(", diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcCommonTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcCommonTest.java index adf4d3d1ff..51a46ccb62 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcCommonTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcCommonTest.java @@ -189,7 +189,8 @@ public class CcCommonTest extends BuildViewTestCase { "archive_in_srcs_test", "cc_test(name = 'archive_in_srcs_test',", " srcs = ['archive_in_srcs_test.cc'],", - " deps = [':archive_in_srcs_lib'])", + " deps = [':archive_in_srcs_lib'],", + " linkstatic = 0,)", "cc_library(name = 'archive_in_srcs_lib',", " srcs = ['libstatic.a', 'libboth.a', 'libboth.so'])"); List<String> artifactNames = baseArtifactNames(getLinkerInputs(archiveInSrcsTest)); diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProviderTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProviderTest.java index 9b8b814a9d..20891db1b5 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProviderTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProviderTest.java @@ -33,61 +33,65 @@ import org.junit.runners.JUnit4; public class CcToolchainProviderTest { @Test public void equalityIsObjectIdentity() throws Exception { - CcToolchainProvider a = new CcToolchainProvider( - null, - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - null, - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - null, - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - null, - PathFragment.EMPTY_FRAGMENT, - CppCompilationContext.EMPTY, - false, - false, - ImmutableMap.<String, String>of(), - ImmutableList.<Artifact>of(), - NestedSetBuilder.<Pair<String, String>>emptySet(Order.COMPILE_ORDER), - null, - ImmutableMap.<String, String>of(), - ImmutableList.<PathFragment>of(), - null); + CcToolchainProvider a = + new CcToolchainProvider( + null, + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + null, + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + null, + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + null, + PathFragment.EMPTY_FRAGMENT, + CppCompilationContext.EMPTY, + false, + false, + ImmutableMap.<String, String>of(), + ImmutableList.<Artifact>of(), + NestedSetBuilder.<Pair<String, String>>emptySet(Order.COMPILE_ORDER), + null, + null, + ImmutableMap.<String, String>of(), + ImmutableList.<PathFragment>of(), + null); - CcToolchainProvider b = new CcToolchainProvider( - null, - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - null, - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - null, - NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), - null, - PathFragment.EMPTY_FRAGMENT, - CppCompilationContext.EMPTY, - false, - false, - ImmutableMap.<String, String>of(), - ImmutableList.<Artifact>of(), - NestedSetBuilder.<Pair<String, String>>emptySet(Order.COMPILE_ORDER), - null, - ImmutableMap.<String, String>of(), - ImmutableList.<PathFragment>of(), - null); + CcToolchainProvider b = + new CcToolchainProvider( + null, + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + null, + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + null, + NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), + null, + PathFragment.EMPTY_FRAGMENT, + CppCompilationContext.EMPTY, + false, + false, + ImmutableMap.<String, String>of(), + ImmutableList.<Artifact>of(), + NestedSetBuilder.<Pair<String, String>>emptySet(Order.COMPILE_ORDER), + null, + null, + ImmutableMap.<String, String>of(), + ImmutableList.<PathFragment>of(), + null); new EqualsTester() .addEqualityGroup(a) diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppOutputGroupsTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppOutputGroupsTest.java index 5110bddd36..9d8e537dab 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppOutputGroupsTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppOutputGroupsTest.java @@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat; import com.google.devtools.build.lib.actions.util.ActionsTestUtil; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -34,8 +33,8 @@ public class CppOutputGroupsTest extends BuildViewTestCase { scratch.file( "a/BUILD", "cc_library(name='lib', srcs=['src.cc'], linkstatic=1, alwayslink=0)", - "filegroup(name='group_archive', srcs=[':lib'], output_group = 'archive')", - "filegroup(name='group_dynamic', srcs=[':lib'], output_group = 'dynamic_library')"); + "filegroup(name='group_archive', srcs=[':lib'], output_group = 'cc_archive')", + "filegroup(name='group_dynamic', srcs=[':lib'], output_group = 'cc_dynamic_library')"); ConfiguredTarget groupArchive = getConfiguredTarget("//a:group_archive"); ConfiguredTarget groupDynamic = getConfiguredTarget("//a:group_dynamic"); @@ -51,8 +50,8 @@ public class CppOutputGroupsTest extends BuildViewTestCase { scratch.file( "a/BUILD", "cc_library(name='lib', srcs=['src.cc'], linkstatic=1, alwayslink=1)", - "filegroup(name='group_archive', srcs=[':lib'], output_group = 'archive')", - "filegroup(name='group_dynamic', srcs=[':lib'], output_group = 'dynamic_library')"); + "filegroup(name='group_archive', srcs=[':lib'], output_group = 'cc_archive')", + "filegroup(name='group_dynamic', srcs=[':lib'], output_group = 'cc_dynamic_library')"); ConfiguredTarget groupArchive = getConfiguredTarget("//a:group_archive"); ConfiguredTarget groupDynamic = getConfiguredTarget("//a:group_dynamic"); @@ -68,16 +67,18 @@ public class CppOutputGroupsTest extends BuildViewTestCase { scratch.file( "a/BUILD", "cc_library(name='lib', srcs=['src.cc'], linkstatic=0, alwayslink=0)", - "filegroup(name='group_archive', srcs=[':lib'], output_group = 'archive')", - "filegroup(name='group_dynamic', srcs=[':lib'], output_group = 'dynamic_library')"); + "filegroup(name='group_archive', srcs=[':lib'], output_group = 'cc_archive')", + "filegroup(name='group_dynamic', srcs=[':lib'], output_group = 'cc_dynamic_library')"); ConfiguredTarget groupArchive = getConfiguredTarget("//a:group_archive"); ConfiguredTarget groupDynamic = getConfiguredTarget("//a:group_dynamic"); assertThat(ActionsTestUtil.prettyArtifactNames(getFilesToBuild(groupArchive))) .containsExactly("a/liblib.a"); + // If supports_interface_shared_objects is true, .ifso could also be generated. + // So we here use contains instead containsExactly. assertThat(ActionsTestUtil.prettyArtifactNames(getFilesToBuild(groupDynamic))) - .containsExactly("a/liblib.so"); + .contains("a/liblib.so"); } @Test @@ -86,15 +87,17 @@ public class CppOutputGroupsTest extends BuildViewTestCase { scratch.file( "a/BUILD", "cc_library(name='lib', srcs=['src.cc'], linkstatic=0, alwayslink=1)", - "filegroup(name='group_archive', srcs=[':lib'], output_group = 'archive')", - "filegroup(name='group_dynamic', srcs=[':lib'], output_group = 'dynamic_library')"); + "filegroup(name='group_archive', srcs=[':lib'], output_group = 'cc_archive')", + "filegroup(name='group_dynamic', srcs=[':lib'], output_group = 'cc_dynamic_library')"); ConfiguredTarget groupArchive = getConfiguredTarget("//a:group_archive"); ConfiguredTarget groupDynamic = getConfiguredTarget("//a:group_dynamic"); assertThat(ActionsTestUtil.prettyArtifactNames(getFilesToBuild(groupArchive))) .containsExactly("a/liblib.lo"); + // If supports_interface_shared_objects is true, .ifso could also be generated. + // So we here use contains instead containsExactly. assertThat(ActionsTestUtil.prettyArtifactNames(getFilesToBuild(groupDynamic))) - .containsExactly("a/liblib.so"); + .contains("a/liblib.so"); } } diff --git a/src/test/py/bazel/BUILD b/src/test/py/bazel/BUILD index f93093b0a1..221d1dc202 100644 --- a/src/test/py/bazel/BUILD +++ b/src/test/py/bazel/BUILD @@ -75,3 +75,26 @@ py_test( srcs = ["launcher_test.py"], deps = [":test_base"], ) + +py_test( + name = "bazel_windows_dynamic_link_test", + size = "medium", + srcs = select({ + "//src:windows": ["bazel_windows_dynamic_link_test.py"], + "//src:windows_msvc": ["bazel_windows_dynamic_link_test.py"], + "//src:windows_msys": ["bazel_windows_dynamic_link_test.py"], + "//conditions:default": ["empty_test.py"], + }), + main = select({ + "//src:windows": "bazel_windows_dynamic_link_test.py", + "//src:windows_msvc": "bazel_windows_dynamic_link_test.py", + "//src:windows_msys": "bazel_windows_dynamic_link_test.py", + "//conditions:default": "empty_test.py", + }), + deps = select({ + "//src:windows": [":test_base"], + "//src:windows_msvc": [":test_base"], + "//src:windows_msys": [":test_base"], + "//conditions:default": [], + }), +) diff --git a/src/test/py/bazel/bazel_windows_dynamic_link_test.py b/src/test/py/bazel/bazel_windows_dynamic_link_test.py new file mode 100644 index 0000000000..33440cd033 --- /dev/null +++ b/src/test/py/bazel/bazel_windows_dynamic_link_test.py @@ -0,0 +1,193 @@ +# Copyright 2017 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. + +import os +import unittest +from src.test.py.bazel import test_base + + +class BazelWindowsDynamicLinkTest(test_base.TestBase): + + def createProjectFiles(self): + self.ScratchFile('WORKSPACE') + self.ScratchFile('BUILD', [ + 'package(', + ' features=["windows_export_all_symbols"]', + ')', + '', + 'cc_library(', + ' name = "A",', + ' srcs = ["a.cc"],', + ' hdrs = ["a.h"],', + ' copts = ["/DCOMPILING_A_DLL"],', + ' features = ["no_windows_export_all_symbols"],', + ')', + '', + 'cc_library(', + ' name = "B",', + ' srcs = ["b.cc"],', + ' hdrs = ["b.h"],', + ' deps = [":A"],', + ' copts = ["/DNO_DLLEXPORT"],', + ')', + '', + 'cc_binary(', + ' name = "C",', + ' srcs = ["c.cc"],', + ' deps = [":A", ":B" ],', + ' linkstatic = 0,', + ')', + ]) + self.ScratchFile('a.cc', [ + '#include <stdio.h>', + '#include "a.h"', + 'int a = 0;', + 'void hello_A() {', + ' a++;', + ' printf("Hello A, %d\\n", a);', + '}', + ]) + self.ScratchFile('b.cc', [ + '#include <stdio.h>', + '#include "a.h"', + '#include "b.h"', + 'void hello_B() {', + ' hello_A();', + ' printf("Hello B\\n");', + '}', + ]) + header_temp = [ + '#ifndef %{name}_H', + '#define %{name}_H', + '', + '#if NO_DLLEXPORT', + ' #define DLLEXPORT', + '#elif COMPILING_%{name}_DLL', + ' #define DLLEXPORT __declspec(dllexport)', + '#else', + ' #define DLLEXPORT __declspec(dllimport)', + '#endif', + '', + 'DLLEXPORT void hello_%{name}();', + '', + '#endif', + ] + self.ScratchFile('a.h', + [line.replace('%{name}', 'A') for line in header_temp]) + self.ScratchFile('b.h', + [line.replace('%{name}', 'B') for line in header_temp]) + + self.ScratchFile('c.cc', [ + '#include <stdio.h>', + '#include "a.h"', + '#include "b.h"', + '', + 'void hello_C() {', + ' hello_A();', + ' hello_B();', + ' printf("Hello C\\n");', + '}', + '', + 'int main() {', + ' hello_C();', + ' return 0;', + '}', + ]) + + def getBazelInfo(self, info_key): + exit_code, stdout, stderr = self.RunBazel(['info', info_key]) + self.AssertExitCode(exit_code, 0, stderr) + return stdout[0] + + def testBuildDynamicLibraryWithUserExportedSymbol(self): + self.createProjectFiles() + bazel_bin = self.getBazelInfo('bazel-bin') + + # //:A export symbols by itself using __declspec(dllexport), so it doesn't + # need Bazel to export symbols using DEF file. + exit_code, _, stderr = self.RunBazel( + ['build', '//:A', '--output_groups=cc_dynamic_library']) + self.AssertExitCode(exit_code, 0, stderr) + + # TODO(pcloudy): change suffixes to .lib and .dll after making DLL + # extensions correct on + # Windows. + import_library = os.path.join(bazel_bin, 'libA.ifso') + shared_library = os.path.join(bazel_bin, 'libA.so') + def_file = os.path.join(bazel_bin, 'A.def') + self.assertTrue(os.path.exists(import_library)) + self.assertTrue(os.path.exists(shared_library)) + # DEF file shouldn't be generated for //:A + self.assertFalse(os.path.exists(def_file)) + + def testBuildDynamicLibraryWithExportSymbolFeature(self): + self.createProjectFiles() + bazel_bin = self.getBazelInfo('bazel-bin') + + # //:B doesn't export symbols by itself, so it need Bazel to export symbols + # using DEF file. + exit_code, _, stderr = self.RunBazel( + ['build', '//:B', '--output_groups=cc_dynamic_library']) + self.AssertExitCode(exit_code, 0, stderr) + + # TODO(pcloudy): change suffixes to .lib and .dll after making DLL + # extensions correct on + # Windows. + import_library = os.path.join(bazel_bin, 'libB.ifso') + shared_library = os.path.join(bazel_bin, 'libB.so') + def_file = os.path.join(bazel_bin, 'B.def') + self.assertTrue(os.path.exists(import_library)) + self.assertTrue(os.path.exists(shared_library)) + # DEF file should be generated for //:B + self.assertTrue(os.path.exists(def_file)) + + # Test build //:B if windows_export_all_symbols feature is disabled by + # no_windows_export_all_symbols. + exit_code, _, stderr = self.RunBazel([ + 'build', '//:B', '--output_groups=cc_dynamic_library', + '--features=no_windows_export_all_symbols' + ]) + self.AssertExitCode(exit_code, 1, stderr) + self.assertIn('output \'libB.ifso\' was not created', ''.join(stderr)) + + def testBuildCcBinaryWithDependenciesDynamicallyLinked(self): + self.createProjectFiles() + bazel_bin = self.getBazelInfo('bazel-bin') + + # Since linkstatic=0 is specified for //:C, it's dependencies should be + # dynamically linked. + exit_code, _, stderr = self.RunBazel(['build', '//:C']) + self.AssertExitCode(exit_code, 0, stderr) + + # TODO(pcloudy): change suffixes to .lib and .dll after making DLL + # extensions correct on + # Windows. + # a_import_library + self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'libA.ifso'))) + # a_shared_library + self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'libA.so'))) + # a_def_file + self.assertFalse(os.path.exists(os.path.join(bazel_bin, 'A.def'))) + # b_import_library + self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'libB.ifso'))) + # b_shared_library + self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'libB.so'))) + # b_def_file + self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'B.def'))) + # c_exe + self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'C.exe'))) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/BUILD b/tools/BUILD index 3270fcba77..a6d6ac37ee 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -19,6 +19,7 @@ filegroup( "//tools/coverage:srcs", "//tools/jdk:srcs", "//tools/launcher:srcs", + "//tools/def_parser:srcs", "//tools/platforms:srcs", "//tools/genrule:srcs", "//tools/cpp:srcs", @@ -50,6 +51,7 @@ filegroup( "//tools/jdk:package-srcs", "//tools/jdk:srcs", "//tools/launcher:srcs", + "//tools/def_parser:srcs", "//tools/platforms:srcs", "//tools/objc:srcs", "//tools/python:srcs", diff --git a/tools/cpp/CROSSTOOL.tpl b/tools/cpp/CROSSTOOL.tpl index 08a3d8004e..2b220f76f2 100644 --- a/tools/cpp/CROSSTOOL.tpl +++ b/tools/cpp/CROSSTOOL.tpl @@ -166,7 +166,7 @@ toolchain { } supports_gold_linker: false supports_start_end_lib: false - supports_interface_shared_objects: false + supports_interface_shared_objects: true supports_incremental_linker: false supports_normalizing_ar: true needsPic: false @@ -238,11 +238,21 @@ toolchain { } } + feature { + name: 'has_configured_linker_path' + } + # This feature indicates strip is not supported, building stripped binary will just result a copy of orignial binary feature { name: 'no_stripping' } + # This feature indicates this is a toolchain targeting Windows. + feature { + name: 'targets_windows' + enabled: true + } + action_config { config_name: 'c-compile' action_name: 'c-compile' @@ -358,6 +368,7 @@ toolchain { implies: 'msvc_env' implies: 'use_linker' implies: 'no_stripping' + implies: 'has_configured_linker_path' } action_config { @@ -618,12 +629,11 @@ toolchain { feature { name: 'input_param_flags' flag_set { - expand_if_all_available: 'library_search_directories' + expand_if_all_available: 'interface_library_output_path' action: 'c++-link-executable' action: 'c++-link-dynamic-library' flag_group { - iterate_over: 'library_search_directories' - flag: "-L%{library_search_directories}" + flag: "/IMPLIB:%{interface_library_output_path}" } } flag_set { @@ -933,6 +943,27 @@ toolchain { } } + feature { + name: 'windows_export_all_symbols' + flag_set { + expand_if_all_available: 'def_file_path' + action: 'c++-link-executable' + action: 'c++-link-dynamic-library' + flag_group { + flag: "/DEF:%{def_file_path}" + # We can specify a different DLL name in DEF file, /ignore:4070 suppresses + # the warning message about DLL name doesn't match the default one. + # See https://msdn.microsoft.com/en-us/library/sfkk2fz7.aspx + flag: "/ignore:4070" + } + } + } + + feature { + name: 'no_windows_export_all_symbols' + } + + linking_mode_flags { mode: DYNAMIC } %{compilation_mode_content} diff --git a/tools/def_parser/BUILD b/tools/def_parser/BUILD new file mode 100644 index 0000000000..7070bebf00 --- /dev/null +++ b/tools/def_parser/BUILD @@ -0,0 +1,18 @@ +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "srcs", + srcs = glob(["**"]), +) + +# cc_toolchain now implicitly depends on @bazel_tools//tools/def_parser:def_parser +# We need to make sure @bazel_tools//tools/def_parser:def_parser is not +# a cc_binary, because otherwise, we'll introduce a cycle in dependency graph: +# .-> @bazel_tools//tools/def_parser:def_parser (cc_binary) +# | cc_toolchain +# `-- @bazel_tools//tools/def_parser:def_parser (cc_binary) + +filegroup( + name = "def_parser", + srcs = ["no_op.bat"], +) diff --git a/tools/def_parser/BUILD.tools b/tools/def_parser/BUILD.tools new file mode 100644 index 0000000000..bd1b891fa8 --- /dev/null +++ b/tools/def_parser/BUILD.tools @@ -0,0 +1,13 @@ +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "def_parser", + srcs = select({ + "//src:host_windows": ["def_parser.exe"], + "//src:host_windows_msvc": ["def_parser.exe"], + "//src:host_windows_msys": ["def_parser.exe"], + "//conditions:default": [ + "no_op.bat", + ], + }), +) diff --git a/tools/def_parser/no_op.bat b/tools/def_parser/no_op.bat new file mode 100644 index 0000000000..b422585831 --- /dev/null +++ b/tools/def_parser/no_op.bat @@ -0,0 +1,17 @@ +:: Copyright 2016 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. + +:: Invoke the python script under pydir with the same basename +@echo OFF +echo IGNORING: %0 %* |