diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java index 234b3aa94068f4..1c090dd508a7a9 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java @@ -1469,6 +1469,17 @@ public boolean isExecutedOnWindows() { .hasConstraintValue(OS_TO_CONSTRAINTS.get(OS.WINDOWS)); } + /** Returns the OS of the execution platform. */ + public OS getExecutionPlatformOs() { + for (var osToConstraint : OS_TO_CONSTRAINTS.entrySet()) { + if (getExecutionPlatform().constraints().hasConstraintValue(osToConstraint.getValue())) { + return osToConstraint.getKey(); + } + } + // Fall back to assuming exec OS == host OS. + return OS.getCurrent(); + } + /** * @return the set of features applicable for the current rule. */ diff --git a/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintConstants.java b/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintConstants.java index 362c31fbecfd53..9b637b966c1ef5 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintConstants.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintConstants.java @@ -30,33 +30,25 @@ public final class ConstraintConstants { // Standard mapping between OS and the corresponding platform constraints. public static final ImmutableMap OS_TO_CONSTRAINTS = - ImmutableMap.builder() - .put( - OS.DARWIN, - ConstraintValueInfo.create( - OS_CONSTRAINT_SETTING, - Label.parseCanonicalUnchecked("@platforms//os:osx"))) - .put( - OS.WINDOWS, - ConstraintValueInfo.create( - OS_CONSTRAINT_SETTING, - Label.parseCanonicalUnchecked("@platforms//os:windows"))) - .put( - OS.FREEBSD, - ConstraintValueInfo.create( - OS_CONSTRAINT_SETTING, - Label.parseCanonicalUnchecked("@platforms//os:freebsd"))) - .put( - OS.OPENBSD, - ConstraintValueInfo.create( - OS_CONSTRAINT_SETTING, - Label.parseCanonicalUnchecked("@platforms//os:openbsd"))) - .put( - OS.UNKNOWN, - ConstraintValueInfo.create( - OS_CONSTRAINT_SETTING, - Label.parseCanonicalUnchecked("@platforms//os:none"))) - .buildOrThrow(); + ImmutableMap.of( + OS.LINUX, + ConstraintValueInfo.create( + OS_CONSTRAINT_SETTING, Label.parseCanonicalUnchecked("@platforms//os:linux")), + OS.DARWIN, + ConstraintValueInfo.create( + OS_CONSTRAINT_SETTING, Label.parseCanonicalUnchecked("@platforms//os:osx")), + OS.WINDOWS, + ConstraintValueInfo.create( + OS_CONSTRAINT_SETTING, Label.parseCanonicalUnchecked("@platforms//os:windows")), + OS.FREEBSD, + ConstraintValueInfo.create( + OS_CONSTRAINT_SETTING, Label.parseCanonicalUnchecked("@platforms//os:freebsd")), + OS.OPENBSD, + ConstraintValueInfo.create( + OS_CONSTRAINT_SETTING, Label.parseCanonicalUnchecked("@platforms//os:openbsd")), + OS.UNKNOWN, + ConstraintValueInfo.create( + OS_CONSTRAINT_SETTING, Label.parseCanonicalUnchecked("@platforms//os:none"))); // No-op constructor to keep this from being instantiated. private ConstraintConstants() {} diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java index 2cbe38afac48a1..dddcab462ff118 100755 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java @@ -75,6 +75,7 @@ import com.google.devtools.build.lib.starlarkbuildapi.cpp.CcModuleApi; import com.google.devtools.build.lib.starlarkbuildapi.cpp.ExtraLinkTimeLibraryApi; import com.google.devtools.build.lib.util.FileTypeSet; +import com.google.devtools.build.lib.util.OS; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.util.StringUtil; import com.google.devtools.build.lib.vfs.PathFragment; @@ -1205,10 +1206,11 @@ public CcToolchainConfigInfo ccToolchainConfigInfoFromStarlark( ImmutableSet featureNames = featureList.stream().map(Feature::getName).collect(toImmutableSet()); + OS execOs = starlarkRuleContext.getRuleContext().getExecutionPlatformOs(); ImmutableList.Builder actionConfigBuilder = ImmutableList.builder(); for (Object actionConfig : actionConfigs) { checkRightStarlarkInfoProvider(actionConfig, "action_configs", "ActionConfigInfo"); - actionConfigBuilder.add(actionConfigFromStarlark((StarlarkInfo) actionConfig)); + actionConfigBuilder.add(actionConfigFromStarlark((StarlarkInfo) actionConfig, execOs)); } ImmutableList actionConfigList = actionConfigBuilder.build(); @@ -1676,7 +1678,8 @@ static FlagSet flagSetFromStarlark(StarlarkInfo flagSetStruct, String actionName * {@link StarlarkInfo}. */ @VisibleForTesting - static CcToolchainFeatures.Tool toolFromStarlark(StarlarkInfo toolStruct) throws EvalException { + static CcToolchainFeatures.Tool toolFromStarlark(StarlarkInfo toolStruct, OS execOs) + throws EvalException { checkRightProviderType(toolStruct, "tool"); String toolPathString = getOptionalFieldFromStarlarkProvider(toolStruct, "path", String.class); @@ -1690,7 +1693,12 @@ static CcToolchainFeatures.Tool toolFromStarlark(StarlarkInfo toolStruct) throws throw infoError(toolStruct, "\"tool\" and \"path\" cannot be set at the same time."); } - toolPath = PathFragment.create(toolPathString); + try { + toolPath = PathFragment.createForOs(toolPathString, execOs); + } catch (PathFragment.PathUnsupportedOnThisOs e) { + throw infoError( + toolStruct, "The 'path' field of tool is not a valid path: %s", e.getMessage()); + } if (toolPath.isEmpty()) { throw infoError(toolStruct, "The 'path' field of tool must be a nonempty string."); } @@ -1723,7 +1731,7 @@ static CcToolchainFeatures.Tool toolFromStarlark(StarlarkInfo toolStruct) throws /** Creates an {@link ActionConfig} from a {@link StarlarkInfo}. */ @VisibleForTesting - static ActionConfig actionConfigFromStarlark(StarlarkInfo actionConfigStruct) + static ActionConfig actionConfigFromStarlark(StarlarkInfo actionConfigStruct, OS execOs) throws EvalException { checkRightProviderType(actionConfigStruct, "action_config"); String actionName = @@ -1748,7 +1756,7 @@ static ActionConfig actionConfigFromStarlark(StarlarkInfo actionConfigStruct) ImmutableList toolStructs = getStarlarkProviderListFromStarlarkField(actionConfigStruct, "tools"); for (StarlarkInfo toolStruct : toolStructs) { - toolBuilder.add(toolFromStarlark(toolStruct)); + toolBuilder.add(toolFromStarlark(toolStruct, execOs)); } ImmutableList.Builder flagSetBuilder = ImmutableList.builder(); diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java index d0c2893cfb7470..1eb4c36b2a4c95 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java @@ -979,21 +979,16 @@ private static void checkToolPath(PathFragment toolPath, CToolchain.Tool.PathOri /** Returns the path to this action's tool relative to the provided crosstool path. */ String getToolPathString(PathFragment ccToolchainPath) { - switch (toolPathOrigin) { - case CROSSTOOL_PACKAGE: + return switch (toolPathOrigin) { + case CROSSTOOL_PACKAGE -> { // Legacy behavior. if (toolPathString == null) { toolPathString = ccToolchainPath.getRelative(toolPathFragment).getSafePathString(); } - return toolPathString; - - case FILESYSTEM_ROOT: // fallthrough. - case WORKSPACE_ROOT: - return toolPathFragment.getSafePathString(); - } - - // Unreached. - throw new IllegalStateException(); + yield toolPathString; + } + case FILESYSTEM_ROOT, WORKSPACE_ROOT -> toolPathFragment.getSafePathString(); + }; } /** diff --git a/src/main/java/com/google/devtools/build/lib/vfs/BUILD b/src/main/java/com/google/devtools/build/lib/vfs/BUILD index b4d6098bbb5b29..50570df6718977 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/BUILD +++ b/src/main/java/com/google/devtools/build/lib/vfs/BUILD @@ -51,6 +51,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/skyframe/serialization", "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec:serialization-constant", "//src/main/java/com/google/devtools/build/lib/util:filetype", + "//src/main/java/com/google/devtools/build/lib/util:os", "//third_party:error_prone_annotations", "//third_party:guava", "//third_party:jsr305", diff --git a/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java b/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java index 5e843acc4a262e..0b17429cb4db3b 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java @@ -86,14 +86,23 @@ public interface OsPathPolicy { boolean isCaseSensitive(); - // We *should* use a case-insensitive policy for OS.DARWIN, but we currently don't handle this. - OsPathPolicy HOST_POLICY = - OS.getCurrent() == OS.WINDOWS ? WindowsOsPathPolicy.INSTANCE : UnixOsPathPolicy.INSTANCE; + OsPathPolicy HOST_POLICY = getFilePathOs(OS.getCurrent()); static OsPathPolicy getFilePathOs() { return HOST_POLICY; } + static OsPathPolicy getFilePathOs(OS os) { + if (os != OS.WINDOWS) { + // We *should* use a case-insensitive policy for OS.DARWIN, but we currently don't handle + // this. + return UnixOsPathPolicy.INSTANCE; + } + return os == OS.getCurrent() + ? WindowsOsPathPolicy.INSTANCE + : WindowsOsPathPolicy.CROSS_PLATFORM_INSTANCE; + } + /** Utilities for implementations of {@link OsPathPolicy}. */ class Utils { /** @@ -135,4 +144,15 @@ static int removeRelativePaths(String[] segments, int starti, boolean isAbsolute return segmentCount; } } + + /** + * Unchecked exception thrown by {@link OsPathPolicy} implementations when a path cannot be + * normalized on the current host OS. + */ + final class UncheckedPathUnsupportedOnThisOsException + extends UnsupportedOperationException { + UncheckedPathUnsupportedOnThisOsException(String message) { + super(message); + } + } } diff --git a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java index 825256467c46dc..8719ffc359988f 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java @@ -68,15 +68,28 @@ public abstract class PathFragment /** Creates a new normalized path fragment. */ public static PathFragment create(String path) { + return createInternal(path, OS); + } + + public static PathFragment createForOs(String path, com.google.devtools.build.lib.util.OS os) + throws PathUnsupportedOnThisOs { + try { + return createInternal(path, OsPathPolicy.getFilePathOs(os)); + } catch (OsPathPolicy.UncheckedPathUnsupportedOnThisOsException e) { + throw new PathUnsupportedOnThisOs(e); + } + } + + private static PathFragment createInternal(String path, OsPathPolicy osPathPolicy) { if (path.isEmpty()) { return EMPTY_FRAGMENT; } - int normalizationLevel = OS.needsToNormalize(path); + int normalizationLevel = osPathPolicy.needsToNormalize(path); String normalizedPath = normalizationLevel != OsPathPolicy.NORMALIZED - ? OS.normalize(path, normalizationLevel) + ? osPathPolicy.normalize(path, normalizationLevel) : path; - int driveStrLength = OS.getDriveStrLength(normalizedPath); + int driveStrLength = osPathPolicy.getDriveStrLength(normalizedPath); return makePathFragment(normalizedPath, driveStrLength); } @@ -126,6 +139,16 @@ public boolean isEmpty() { */ public abstract int getDriveStrLength(); + /** + * Thrown by {@link #createForOs(String, com.google.devtools.build.lib.util.OS)} * when a path + * cannot be normalized on the current host OS. + */ + public static final class PathUnsupportedOnThisOs extends Exception { + private PathUnsupportedOnThisOs(OsPathPolicy.UncheckedPathUnsupportedOnThisOsException e) { + super(e.getMessage(), e); + } + } + private static final class RelativePathFragment extends PathFragment { // DON'T add any fields here unless you know what you are doing. Adding another field will // increase the shallow heap of a RelativePathFragment instance beyond the current value of 16 diff --git a/src/main/java/com/google/devtools/build/lib/vfs/WindowsOsPathPolicy.java b/src/main/java/com/google/devtools/build/lib/vfs/WindowsOsPathPolicy.java index 86908cfcb1664c..93d73486214ca5 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/WindowsOsPathPolicy.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/WindowsOsPathPolicy.java @@ -23,7 +23,11 @@ @VisibleForTesting class WindowsOsPathPolicy implements OsPathPolicy { - static final WindowsOsPathPolicy INSTANCE = new WindowsOsPathPolicy(); + static final WindowsOsPathPolicy INSTANCE = + new WindowsOsPathPolicy(new DefaultShortPathResolver()); + + static final WindowsOsPathPolicy CROSS_PLATFORM_INSTANCE = + new WindowsOsPathPolicy(new CrossPlatformShortPathResolver()); static final int NEEDS_SHORT_PATH_NORMALIZATION = NEEDS_NORMALIZE + 1; @@ -33,7 +37,7 @@ class WindowsOsPathPolicy implements OsPathPolicy { private final ShortPathResolver shortPathResolver; interface ShortPathResolver { - String resolveShortPath(String path); + String resolveShortPath(String path) throws UncheckedPathUnsupportedOnThisOsException; } static class DefaultShortPathResolver implements ShortPathResolver { @@ -47,10 +51,15 @@ public String resolveShortPath(String path) { } } - WindowsOsPathPolicy() { - this(new DefaultShortPathResolver()); + static class CrossPlatformShortPathResolver implements ShortPathResolver { + @Override + public String resolveShortPath(String path) { + throw new UncheckedPathUnsupportedOnThisOsException( + "Windows short paths can only be resolved on a Windows host: " + path); + } } + @VisibleForTesting WindowsOsPathPolicy(ShortPathResolver shortPathResolver) { this.shortPathResolver = shortPathResolver; } diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD index e77290bfeba9a4..a90f549eb83886 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD @@ -674,6 +674,7 @@ java_test( "//src/main/java/com/google/devtools/build/lib/rules/cpp", "//src/main/java/com/google/devtools/build/lib/skyframe:bzl_load_value", "//src/main/java/com/google/devtools/build/lib/util", + "//src/main/java/com/google/devtools/build/lib/util:os", "//src/main/java/com/google/devtools/build/lib/vfs", "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment", "//src/main/java/net/starlark/java/eval", diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java index 613d9b1b84572f..49be08ddce8b28 100755 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.rules.cpp; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.TruthJUnit.assume; import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.baseArtifactNames; import static com.google.devtools.build.lib.rules.cpp.SolibSymlinkAction.MAX_FILENAME_LENGTH; import static com.google.devtools.build.lib.skyframe.BzlLoadValue.keyForBuild; @@ -58,6 +59,7 @@ import com.google.devtools.build.lib.rules.cpp.CcToolchainVariables.StringValueParser; import com.google.devtools.build.lib.testutil.Scratch; import com.google.devtools.build.lib.testutil.TestConstants; +import com.google.devtools.build.lib.util.OS; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.PathFragment; @@ -3438,7 +3440,8 @@ public void testTool_withFeatures_mustBeWithFeatureSet() throws Exception { StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); assertThat(toolStruct).isNotNull(); EvalException e = - assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); + assertThrows( + EvalException.class, () -> CcModule.toolFromStarlark(toolStruct, OS.getCurrent())); assertThat(e) .hasMessageThat() .contains("Expected object of type 'with_feature_set', received 'struct'"); @@ -3457,7 +3460,8 @@ public void testTool_requirements_mustBeString() throws Exception { StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); assertThat(toolStruct).isNotNull(); EvalException e = - assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); + assertThrows( + EvalException.class, () -> CcModule.toolFromStarlark(toolStruct, OS.getCurrent())); assertThat(e) .hasMessageThat() .contains("at index 0 of execution_requirements, got element of type struct, want string"); @@ -3475,7 +3479,7 @@ public void testTool() throws Exception { ConfiguredTarget t = getConfiguredTarget("//six:a"); StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); assertThat(toolStruct).isNotNull(); - Tool tool = CcModule.toolFromStarlark(toolStruct); + Tool tool = CcModule.toolFromStarlark(toolStruct, OS.getCurrent()); assertThat(tool.getExecutionRequirements()).containsExactly("a", "b"); assertThat(tool.getToolPathString(PathFragment.EMPTY_FRAGMENT)).isEqualTo("/a/b/c"); assertThat(tool.getWithFeatureSetSets()) @@ -3507,7 +3511,8 @@ public void testCustomTool_path_nonEmpty() throws Exception { StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); assertThat(toolStruct).isNotNull(); EvalException e = - assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); + assertThrows( + EvalException.class, () -> CcModule.toolFromStarlark(toolStruct, OS.getCurrent())); assertThat(e).hasMessageThat().contains("The 'path' field of tool must be a nonempty string."); } @@ -3521,7 +3526,8 @@ public void testCustomTool_path_mustBeString() throws Exception { StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); assertThat(toolStruct).isNotNull(); EvalException e = - assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); + assertThrows( + EvalException.class, () -> CcModule.toolFromStarlark(toolStruct, OS.getCurrent())); assertThat(e).hasMessageThat().contains("Field 'path' is not of 'java.lang.String' type."); } @@ -3535,7 +3541,8 @@ public void testCustomTool_withFeatures_mustBeList() throws Exception { StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); assertThat(toolStruct).isNotNull(); EvalException e = - assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); + assertThrows( + EvalException.class, () -> CcModule.toolFromStarlark(toolStruct, OS.getCurrent())); assertThat(e).hasMessageThat().contains("for with_features, got struct, want sequence"); } @@ -3552,7 +3559,8 @@ public void testCustomTool_withFeatures_mustBeWithFeatureSet() throws Exception StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); assertThat(toolStruct).isNotNull(); EvalException e = - assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); + assertThrows( + EvalException.class, () -> CcModule.toolFromStarlark(toolStruct, OS.getCurrent())); assertThat(e) .hasMessageThat() .contains("Expected object of type 'with_feature_set', received 'struct'"); @@ -3568,7 +3576,8 @@ public void testCustomTool_executionRequirements_mustBeList() throws Exception { StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); assertThat(toolStruct).isNotNull(); EvalException e = - assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); + assertThrows( + EvalException.class, () -> CcModule.toolFromStarlark(toolStruct, OS.getCurrent())); assertThat(e) .hasMessageThat() .contains("for execution_requirements, got string, want sequence"); @@ -3584,12 +3593,108 @@ public void testCustomTool_executionRequirements_mustBeString() throws Exception StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); assertThat(toolStruct).isNotNull(); EvalException e = - assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct)); + assertThrows( + EvalException.class, () -> CcModule.toolFromStarlark(toolStruct, OS.getCurrent())); assertThat(e) .hasMessageThat() .contains("at index 0 of execution_requirements, got element of type struct, want string"); } + @Test + public void testCustomTool_windowsAbsolutePath() throws Exception { + loadCcToolchainConfigLib(); + createCustomToolRule( + "seven", + /* path= */ Starlark.repr( + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.39.33519\\bin/HostX64/x64/cl.exe"), + /* withFeatures= */ "[]", + /* requirements= */ "[]"); + + ConfiguredTarget t = getConfiguredTarget("//seven:a"); + StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); + assertThat(toolStruct).isNotNull(); + Tool tool = CcModule.toolFromStarlark(toolStruct, OS.WINDOWS); + assertThat(tool.getToolPathString(PathFragment.create("external/my_toolchain"))) + .isEqualTo( + "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.39.33519/bin/HostX64/x64/cl.exe"); + } + + @Test + public void testCustomTool_windowsRelativePath() throws Exception { + loadCcToolchainConfigLib(); + createCustomToolRule( + "seven", + /* path= */ Starlark.repr("bin\\HostX64\\x64/cl.exe"), + /* withFeatures= */ "[]", + /* requirements= */ "[]"); + + ConfiguredTarget t = getConfiguredTarget("//seven:a"); + StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); + assertThat(toolStruct).isNotNull(); + Tool tool = CcModule.toolFromStarlark(toolStruct, OS.WINDOWS); + assertThat(tool.getToolPathString(PathFragment.create("external/my_toolchain"))) + .isEqualTo("external/my_toolchain/bin/HostX64/x64/cl.exe"); + } + + @Test + public void testCustomTool_windowsShortPath_failsOnNonWindowsHostOs() throws Exception { + assume().that(OS.getCurrent()).isNotEqualTo(OS.WINDOWS); + + loadCcToolchainConfigLib(); + createCustomToolRule( + "seven", + /* path= */ Starlark.repr( + "C:\\PROGRA~1\\MICROS~1\\2022\\COMMUN~1\\VC\\TOOLS\\MSVC\\14.39.33519\\BIN\\HOSTX64\\X64\\CL.EXE"), + /* withFeatures= */ "[]", + /* requirements= */ "[]"); + + ConfiguredTarget t = getConfiguredTarget("//seven:a"); + StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); + assertThat(toolStruct).isNotNull(); + var e = + assertThrows(EvalException.class, () -> CcModule.toolFromStarlark(toolStruct, OS.WINDOWS)); + assertThat(e) + .hasMessageThat() + .contains( + "The 'path' field of tool is not a valid path: Windows short paths can only be resolved" + + " on a Windows host:" + + " C:\\PROGRA~1\\MICROS~1\\2022\\COMMUN~1\\VC\\TOOLS\\MSVC\\14.39.33519\\BIN\\HOSTX64\\X64\\CL.EXE"); + } + + @Test + public void testCustomTool_unixAbsolutePath() throws Exception { + loadCcToolchainConfigLib(); + createCustomToolRule( + "seven", + /* path= */ Starlark.repr("/usr/bin/gcc"), + /* withFeatures= */ "[]", + /* requirements= */ "[]"); + + ConfiguredTarget t = getConfiguredTarget("//seven:a"); + StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); + assertThat(toolStruct).isNotNull(); + Tool tool = CcModule.toolFromStarlark(toolStruct, OS.LINUX); + assertThat(tool.getToolPathString(PathFragment.create("external/my_toolchain"))) + .isEqualTo("/usr/bin/gcc"); + } + + @Test + public void testCustomTool_unixRelativePath() throws Exception { + loadCcToolchainConfigLib(); + createCustomToolRule( + "seven", + /* path= */ Starlark.repr("bin/gcc"), + /* withFeatures= */ "[]", + /* requirements= */ "[]"); + + ConfiguredTarget t = getConfiguredTarget("//seven:a"); + StarlarkInfo toolStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("tool"); + assertThat(toolStruct).isNotNull(); + Tool tool = CcModule.toolFromStarlark(toolStruct, OS.LINUX); + assertThat(tool.getToolPathString(PathFragment.create("external/my_toolchain"))) + .isEqualTo("external/my_toolchain/bin/gcc"); + } + private void createCustomToolRule( String pkg, String path, String withFeatures, String requirements) throws Exception { scratch.file( @@ -3890,7 +3995,8 @@ public void testActionConfig_tools_mustBeTool() throws Exception { assertThat(actionConfigStruct).isNotNull(); EvalException e = assertThrows( - EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); + EvalException.class, + () -> CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent())); assertThat(e) .hasMessageThat() .contains("Expected object of type 'tool', received 'with_feature_set'"); @@ -3912,7 +4018,8 @@ public void testActionConfig_flagSets_mustBeFlagSet() throws Exception { assertThat(actionConfigStruct).isNotNull(); EvalException e = assertThrows( - EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); + EvalException.class, + () -> CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent())); assertThat(e).hasMessageThat().contains("Expected object of type 'flag_set', received 'tool'"); } @@ -3949,7 +4056,8 @@ public void testActionConfig_implies_mustContainString() throws Exception { assertThat(actionConfigStruct).isNotNull(); EvalException e = assertThrows( - EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); + EvalException.class, + () -> CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent())); assertThat(e) .hasMessageThat() .contains("at index 0 of implies, got element of type struct, want string"); @@ -3971,7 +4079,8 @@ public void testActionConfig_implies_mustContainString_notStruct() throws Except assertThat(actionConfigStruct).isNotNull(); EvalException e = assertThrows( - EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); + EvalException.class, + () -> CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent())); assertThat(e) .hasMessageThat() .contains("at index 0 of implies, got element of type struct, want string"); @@ -3991,7 +4100,7 @@ public void testActionConfig() throws Exception { ConfiguredTarget t = getConfiguredTarget("//eight:a"); StarlarkInfo actionConfigStruct = (StarlarkInfo) getMyInfoFromTarget(t).getValue("config"); assertThat(actionConfigStruct).isNotNull(); - ActionConfig a = CcModule.actionConfigFromStarlark(actionConfigStruct); + ActionConfig a = CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent()); assertThat(a).isNotNull(); assertThat(a.getActionName()).isEqualTo("actionname32._++-"); assertThat(a.getImplies()).containsExactly("a", "b").inOrder(); @@ -4015,7 +4124,8 @@ public void testActionConfig_actionName_validChars_notUpper() throws Exception { assertThat(actionConfigStruct).isNotNull(); EvalException e = assertThrows( - EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); + EvalException.class, + () -> CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent())); assertThat(e) .hasMessageThat() .contains( @@ -4039,7 +4149,8 @@ public void testActionConfig_actionName_validChars_notWhitespace() throws Except assertThat(actionConfigStruct).isNotNull(); EvalException e = assertThrows( - EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); + EvalException.class, + () -> CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent())); assertThat(e) .hasMessageThat() .contains( @@ -4083,7 +4194,8 @@ public void testCustomActionConfig_actionName_mustBeString() throws Exception { assertThat(actionConfigStruct).isNotNull(); EvalException e = assertThrows( - EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); + EvalException.class, + () -> CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent())); assertThat(e) .hasMessageThat() .contains("Field 'action_name' is not of 'java.lang.String' type."); @@ -4105,7 +4217,8 @@ public void testCustomActionConfig_enabled_mustBeBool() throws Exception { assertThat(actionConfigStruct).isNotNull(); EvalException e = assertThrows( - EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); + EvalException.class, + () -> CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent())); assertThat(e).hasMessageThat().contains("Field 'enabled' is not of 'java.lang.Boolean' type."); } @@ -4125,7 +4238,8 @@ public void testCustomActionConfig_tools_mustBeList() throws Exception { assertThat(actionConfigStruct).isNotNull(); EvalException e = assertThrows( - EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); + EvalException.class, + () -> CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent())); assertThat(e).hasMessageThat().contains("for tools, got struct, want sequence"); } @@ -4145,7 +4259,8 @@ public void testCustomActionConfig_flagSets_mustBeList() throws Exception { assertThat(actionConfigStruct).isNotNull(); EvalException e = assertThrows( - EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); + EvalException.class, + () -> CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent())); assertThat(e).hasMessageThat().contains("for flag_sets, got bool, want sequence"); } @@ -4165,7 +4280,8 @@ public void testCustomActionConfig_implies_mustBeList() throws Exception { assertThat(actionConfigStruct).isNotNull(); EvalException e = assertThrows( - EvalException.class, () -> CcModule.actionConfigFromStarlark(actionConfigStruct)); + EvalException.class, + () -> CcModule.actionConfigFromStarlark(actionConfigStruct, OS.getCurrent())); assertThat(e).hasMessageThat().contains("for implies, got struct, want sequence"); }