aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Yun Peng <pcloudy@google.com>2017-09-15 15:59:14 +0200
committerGravatar László Csomor <laszlocsomor@google.com>2017-09-18 11:25:37 +0200
commit394211bf88b88c25036429e99894fa7c60eaaace (patch)
treea5a99354a9f3b8530bb0d98d61d8844b21348ecd
parentf8480fac37692083b2f9c4e69b9b9e9e2c9b1853 (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
-rw-r--r--src/BUILD4
-rw-r--r--src/create_embedded_tools.py1
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java74
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCcTestRule.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java33
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java20
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java12
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java24
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java24
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java92
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java16
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java5
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/CcCommonTest.java3
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProviderTest.java112
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/CppOutputGroupsTest.java25
-rw-r--r--src/test/py/bazel/BUILD23
-rw-r--r--src/test/py/bazel/bazel_windows_dynamic_link_test.py193
-rw-r--r--tools/BUILD2
-rw-r--r--tools/cpp/CROSSTOOL.tpl39
-rw-r--r--tools/def_parser/BUILD18
-rw-r--r--tools/def_parser/BUILD.tools13
-rw-r--r--tools/def_parser/no_op.bat17
23 files changed, 640 insertions, 120 deletions
diff --git a/src/BUILD b/src/BUILD
index 039baeb09a..a8bda8acc6 100644
--- a/src/BUILD
+++ b/src/BUILD
@@ -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 %*