зеркало из https://github.com/microsoft/BuildXL.git
Merged PR 670983: Enable JS related tests on Linux
Enable JS related tests on Linux Related work items: #1965668
This commit is contained in:
Родитель
16998d8927
Коммит
8fc40aae60
|
@ -15,6 +15,9 @@ namespace Node {
|
|||
|
||||
const nodeExecutablesDir : Directory = d`${getNodeTool().exe.parent}`;
|
||||
|
||||
@@public
|
||||
export const nodePackage = getNodePackage();
|
||||
|
||||
/**
|
||||
* Self-contained node executables. Platform dependent.
|
||||
*/
|
||||
|
@ -22,7 +25,6 @@ namespace Node {
|
|||
export const nodeExecutables : Deployment.Deployable = getNodeExecutables();
|
||||
|
||||
function getNodeExecutables() : Deployment.Deployable {
|
||||
const nodePackage = getNodePackage();
|
||||
const relativePath = nodePackage.root.path.getRelative(nodeExecutablesDir.path);
|
||||
|
||||
return Deployment.createDeployableOpaqueSubDirectory(
|
||||
|
@ -58,7 +60,7 @@ namespace Node {
|
|||
return Transformer.execute(execArgs);
|
||||
}
|
||||
|
||||
const nodeVersion = "v18.3.0";
|
||||
const nodeVersion = "v18.6.0";
|
||||
const nodeWinDir = `node-${nodeVersion}-win-x64`;
|
||||
const nodeOsxDir = `node-${nodeVersion}-darwin-x64`;
|
||||
const nodeLinuxDir = `node-${nodeVersion}-linux-x64`;
|
||||
|
@ -84,7 +86,49 @@ namespace Node {
|
|||
Contract.fail(`The current NodeJs package doesn't support the current OS: ${host.os}. Ensure you run on a supported OS -or- update the NodeJs package to have the version embedded.`);
|
||||
}
|
||||
|
||||
return pkgContents;
|
||||
const outDir = Context.getNewOutputDirectory("executable-npm");
|
||||
|
||||
const pkgRoot = d`${pkgContents.root}/${getNodePackageRoot()}`;
|
||||
|
||||
// For Windows, we just need to get rid of the root folder (which for node it always contains the version number). Unfortunately there is no
|
||||
// way to get a nested opaque directory for an exclusive opaque (only for shared opaques)
|
||||
if (host.os === "win")
|
||||
{
|
||||
return Transformer.copyDirectory({sourceDir: pkgRoot, targetDir: outDir, dependencies: [pkgContents], recursive: true});
|
||||
}
|
||||
|
||||
// For linux/mac we need to create an executable npm symlink alongside node
|
||||
// The npm file does come with the linux package in the form of a symlink, but our current
|
||||
// targz expander doesn't handle it well
|
||||
|
||||
const npmExe = p`${outDir}/bin/npm`;
|
||||
|
||||
const result = Transformer.execute({
|
||||
tool: {
|
||||
exe: f`/bin/bash`,
|
||||
dependsOnCurrentHostOSDirectories: true
|
||||
},
|
||||
workingDirectory: outDir,
|
||||
arguments: [
|
||||
Cmd.argument("-c"),
|
||||
Cmd.rawArgument('"'),
|
||||
// Copy the contents to its final destination
|
||||
Cmd.args([ "rsync", "-arvh", Cmd.join("", [Artifact.none(pkgRoot), '/']), Artifact.none(outDir)]),
|
||||
Cmd.rawArgument(" && "),
|
||||
// Create and npm node executable
|
||||
Cmd.args([ "echo", "'#!/usr/bin/env", "node'", ">", Artifact.none(npmExe) ]),
|
||||
Cmd.rawArgument(" && "),
|
||||
Cmd.args([ "echo", "\"require(\'../lib/node_modules/npm/lib/cli.js\')(process)\"", ">>", Artifact.none(npmExe) ]),
|
||||
Cmd.rawArgument(" && "),
|
||||
// Set the right permissions
|
||||
Cmd.args([ "chmod", "u+x", Artifact.none(npmExe) ]),
|
||||
Cmd.rawArgument('"')
|
||||
],
|
||||
dependencies: [pkgContents],
|
||||
outputs: [outDir]
|
||||
});
|
||||
|
||||
return result.getOutputDirectory(outDir);
|
||||
}
|
||||
|
||||
function getNodeTool() : Transformer.ToolDefinition {
|
||||
|
@ -93,17 +137,15 @@ namespace Node {
|
|||
Contract.assert(host.cpuArchitecture === "x64", "Only 64bit versions supported.");
|
||||
|
||||
let executable : RelativePath = undefined;
|
||||
let pkgContents : OpaqueDirectory = getNodePackage();
|
||||
let pkgContents : OpaqueDirectory = nodePackage;
|
||||
|
||||
switch (host.os) {
|
||||
case "win":
|
||||
executable = r`${nodeWinDir}/node.exe`;
|
||||
executable = r`node.exe`;
|
||||
break;
|
||||
case "macOS":
|
||||
executable = r`${nodeOsxDir}/bin/node`;
|
||||
break;
|
||||
case "unix":
|
||||
executable = r`${nodeLinuxDir}/bin/node`;
|
||||
executable = r`bin/node`;
|
||||
break;
|
||||
default:
|
||||
Contract.fail(`The current NodeJs package doesn't support the current OS: ${host.os}. Esure you run on a supported OS -or- update the NodeJs package to have the version embdded.`);
|
||||
|
@ -126,20 +168,17 @@ namespace Node {
|
|||
Contract.assert(host.cpuArchitecture === "x64", "Only 64bit versions supported.");
|
||||
|
||||
let executable : RelativePath = undefined;
|
||||
let pkgContents : StaticDirectory = undefined;
|
||||
let pkgContents : StaticDirectory = nodePackage;
|
||||
|
||||
switch (host.os) {
|
||||
case "win":
|
||||
pkgContents = importFrom("NodeJs.win-x64").extracted;
|
||||
executable = r`${nodeWinDir}/node_modules/npm/bin/npm-cli.js`;
|
||||
executable = r`node_modules/npm/bin/npm-cli.js`;
|
||||
break;
|
||||
case "macOS":
|
||||
pkgContents = importFrom("NodeJs.osx-x64").extracted;
|
||||
executable = r`${nodeOsxDir}/lib/node_modules/npm/bin/npm-cli.js`;
|
||||
executable = r`lib/node_modules/npm/bin/npm-cli.js`;
|
||||
break;
|
||||
case "unix":
|
||||
pkgContents = importFrom("NodeJs.linux-x64").extracted;
|
||||
executable = r`${nodeLinuxDir}/lib/node_modules/npm/bin/npm-cli.js`;
|
||||
executable = r`lib/node_modules/npm/bin/npm-cli.js`;
|
||||
break;
|
||||
default:
|
||||
Contract.fail(`The current NodeJs package doesn't support the current OS: ${host.os}. Ensure you run on a supported OS -or- update the NodeJs package to have the version embedded.`);
|
||||
|
@ -153,24 +192,48 @@ namespace Node {
|
|||
*/
|
||||
@@public
|
||||
export function getNpmTool() : Transformer.ToolDefinition {
|
||||
return Npm.getNpmTool(nodePackage, getPathToNpm());
|
||||
}
|
||||
|
||||
function getPathToNpm() : RelativePath {
|
||||
const host = Context.getCurrentHost();
|
||||
let executable : RelativePath = undefined;
|
||||
|
||||
switch (host.os) {
|
||||
case "win":
|
||||
executable = r`${nodeWinDir}/npm.cmd`;
|
||||
executable = r`npm.cmd`;
|
||||
break;
|
||||
case "macOS":
|
||||
executable = r`${nodeOsxDir}/bin/npm`;
|
||||
executable = r`lib/node_modules/npm/bin/npm`;
|
||||
break;
|
||||
case "unix":
|
||||
executable = r`${nodeLinuxDir}/bin/npm`;
|
||||
executable = r`lib/node_modules/npm/bin/npm`;
|
||||
break;
|
||||
default:
|
||||
Contract.fail(`The current NodeJs package doesn't support the current OS: ${host.os}. Ensure you run on a supported OS -or- update the NodeJs package to have the version embedded.`);
|
||||
}
|
||||
|
||||
return Npm.getNpmTool(getNodePackage(), executable);
|
||||
|
||||
return executable;
|
||||
}
|
||||
|
||||
function getNodePackageRoot(): RelativePath{
|
||||
const host = Context.getCurrentHost();
|
||||
let relativePath : RelativePath = undefined;
|
||||
switch (host.os) {
|
||||
case "win":
|
||||
relativePath = r`${nodeWinDir}`;
|
||||
break;
|
||||
case "macOS":
|
||||
relativePath = r`${nodeOsxDir}`;
|
||||
break;
|
||||
case "unix":
|
||||
relativePath = r`${nodeLinuxDir}`;
|
||||
break;
|
||||
default:
|
||||
Contract.fail(`The current NodeJs package doesn't support the current OS: ${host.os}.`);
|
||||
}
|
||||
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
@@public
|
||||
|
|
|
@ -3,23 +3,40 @@
|
|||
|
||||
import {Transformer} from "Sdk.Transformers";
|
||||
|
||||
// Yarn tgz sadly includes the version as a top level directory
|
||||
const yarnVersion = "yarn-v1.22.19";
|
||||
|
||||
/**
|
||||
* Returns a static directory containing a valid Yarn installation.
|
||||
*/
|
||||
@@public
|
||||
export function getYarn() : StaticDirectory {
|
||||
if (Environment.getFlag("[Sdk.BuildXL]microsoftInternal")) {
|
||||
// Internally in Microsoft we use a nuget package that contains Yarn.
|
||||
return Transformer.reSealPartialDirectory(importFrom("NPM.OnCloudbuild").Contents.all, r`tools/Yarn`);
|
||||
const yarnPackage = importFrom("YarnTool").yarnPackage;
|
||||
|
||||
const yarnDir = Context.getNewOutputDirectory("yarn-package");
|
||||
const isWinOs = Context.getCurrentHost().os === "win";
|
||||
|
||||
// 'yarn', the linux executable that comes with the package, doesn't have the execution permissions set
|
||||
let executableYarn = undefined;
|
||||
if (!isWinOs) {
|
||||
const yarnExe = yarnPackage.assertExistence(r`${yarnVersion}/bin/yarn`);
|
||||
executableYarn = Transformer.makeExecutable(yarnExe, p`${yarnDir}/${yarnVersion}/bin/yarn`);
|
||||
}
|
||||
|
||||
// For the public build, we require Yarn to be installed
|
||||
const installedYarnLocation = d`${Context.getMount("ProgramFilesX86").path}/Yarn`;
|
||||
const packageJson = f`${installedYarnLocation}/package.json`;
|
||||
if (!File.exists(packageJson))
|
||||
{
|
||||
Contract.fail(`Could not find Yarn installed. File '${packageJson.toDiagnosticString()}' does not exist.`);
|
||||
}
|
||||
const sharedOpaqueYarnPackage = Transformer.copyDirectory({
|
||||
sourceDir: yarnPackage.root,
|
||||
targetDir: yarnDir,
|
||||
dependencies: [yarnPackage, executableYarn],
|
||||
recursive: true,
|
||||
excludePattern: isWinOs? undefined : "yarn"});
|
||||
|
||||
return Transformer.sealDirectory(installedYarnLocation, globR(installedYarnLocation));
|
||||
const finalDeployment = Context.getNewOutputDirectory("yarn-package-final");
|
||||
|
||||
const result = Transformer.copyDirectory({
|
||||
sourceDir: d`${sharedOpaqueYarnPackage.root}/${yarnVersion}`,
|
||||
targetDir: finalDeployment,
|
||||
dependencies: [sharedOpaqueYarnPackage, executableYarn],
|
||||
recursive: true});
|
||||
|
||||
return result;
|
||||
}
|
|
@ -14,8 +14,10 @@ namespace Transformer {
|
|||
export interface CopyDirectoryArguments {
|
||||
sourceDir: Directory;
|
||||
targetDir: Directory;
|
||||
dependencies?: StaticDirectory[];
|
||||
dependencies?: (StaticDirectory | File)[];
|
||||
pattern?: string;
|
||||
// Only available on Linux/Mac
|
||||
excludePattern?: string;
|
||||
recursive?: boolean;
|
||||
keepOutputsWritable?: boolean
|
||||
}
|
||||
|
@ -28,6 +30,11 @@ namespace Transformer {
|
|||
*/
|
||||
@@public
|
||||
export function copyDirectory(arguments: CopyDirectoryArguments): SharedOpaqueDirectory {
|
||||
|
||||
if (Context.getCurrentHost().os === "win") {
|
||||
Contract.assert(arguments.excludePattern === undefined, "Exclude pattern is not supported on Windows but was provided.");
|
||||
}
|
||||
|
||||
const args: Transformer.ExecuteArguments = Context.getCurrentHost().os === "win"
|
||||
? <Transformer.ExecuteArguments>{
|
||||
tool: {
|
||||
|
@ -69,6 +76,7 @@ namespace Transformer {
|
|||
arguments: [
|
||||
Cmd.argument(arguments.recursive === false ? "-avh" : "-arvh"),
|
||||
Cmd.option("--include ", arguments.pattern),
|
||||
Cmd.option("--exclude ", arguments.excludePattern),
|
||||
Cmd.argument(Cmd.join("", [ Artifact.none(arguments.sourceDir), '/' ])),
|
||||
Cmd.argument(Artifact.none(arguments.targetDir)),
|
||||
],
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Transformer {
|
||||
|
||||
/**
|
||||
* Copies the input to the provided output path setting the result to the given unix-style permissions
|
||||
* E.g. chmod("u+x", input, output)
|
||||
* On Windows, it just does a file copy without changing any file permissions
|
||||
*/
|
||||
@@public
|
||||
export function chmod(permissions: string, input: File, output: Path): File {
|
||||
const currentHost = Context.getCurrentHost();
|
||||
|
||||
if (currentHost.os === "macOS" || currentHost.os === "unix") {
|
||||
const result = Transformer.execute({
|
||||
tool: {
|
||||
exe: f`/bin/bash`,
|
||||
dependsOnCurrentHostOSDirectories: true
|
||||
},
|
||||
workingDirectory: d`${output.parent}`,
|
||||
arguments: [
|
||||
Cmd.argument("-c"),
|
||||
Cmd.rawArgument('"'),
|
||||
Cmd.args([ "cp", Artifact.input(input), Artifact.output(output) ]),
|
||||
Cmd.rawArgument(" && "),
|
||||
Cmd.args([ "chmod", permissions, Artifact.none(output) ]),
|
||||
Cmd.rawArgument('"')
|
||||
]
|
||||
});
|
||||
|
||||
return result.getOutputFile(output);
|
||||
}
|
||||
else {
|
||||
return Transformer.copyFile(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls chmod("u+x", input, output)
|
||||
*/
|
||||
@@public
|
||||
export function makeExecutable(input: File, output: Path): File {
|
||||
return chmod("u+x", input, output);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using BuildXL.Utilities;
|
||||
using BuildXL.Utilities.Configuration;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Test.BuildXL.TestUtilities.XUnit;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using LogEventId = global::BuildXL.FrontEnd.JavaScript.Tracing.LogEventId;
|
||||
|
@ -46,9 +47,10 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[""../output/Dir""]", new[] { "src/output/dir"})]
|
||||
[InlineData(@"[""../output/Dir"", ""../another/dir""]", new[] { "src/output/dir", "src/another/dir" })]
|
||||
[InlineData(@"[""<workspaceDir>/output"", ""C:\\foo""]", new[] { "output", "C:\\foo" })]
|
||||
[InlineData(@"[""../output/dir""]", new[] { "src/output/dir"})]
|
||||
[InlineData(@"[""../output/dir"", ""../another/dir""]", new[] { "src/output/dir", "src/another/dir" })]
|
||||
[InlineDataIfSupported(requiresWindowsBasedOperatingSystem: true, data: new object[] {@"[""<workspaceDir>/output"", ""C:\\foo""]", new[] { "output", "C:\\foo" }})]
|
||||
[InlineDataIfSupported(requiresUnixBasedOperatingSystem: true, data: new object[] {@"[""<workspaceDir>/output"", ""/foo""]", new[] { "output", "/foo" }})]
|
||||
public void PathPatternsAreHonored(string outputDirectoriesJSON, string[] expectedOutputDirectories)
|
||||
{
|
||||
var config = Build()
|
||||
|
@ -101,7 +103,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
|
||||
[Theory]
|
||||
[InlineData(@"undefined")]
|
||||
[InlineData(@"""invalid|path""")]
|
||||
[InlineDataIfSupported(requiresWindowsBasedOperatingSystem: true, data: new object[] {@"""invalid|path"""})]
|
||||
[InlineData(@"invalid {{ json")]
|
||||
public void MalformedConfigurationFileIsHandled(string outputDirectories)
|
||||
{
|
||||
|
|
|
@ -88,8 +88,9 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
[Fact]
|
||||
public void ComplexCustomCommand()
|
||||
{
|
||||
var path = X("/c/absolute/path");
|
||||
// Exercise custom commands with other types
|
||||
var config = Build(customRushCommands: "[{command: 'build', extraArguments: ['--test', a`atom`, r`relative/path`, p`C:/absolute/path`]}]")
|
||||
var config = Build(customRushCommands: $"[{{command: 'build', extraArguments: ['--test', a`atom`, r`relative/path`, p`{path}`]}}]")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "execute") })
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
|
@ -101,7 +102,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
|
||||
var pip = result.EngineState.RetrieveProcess("@ms/project-A", "build");
|
||||
Assert.Contains(
|
||||
$"execute --test atom {RelativePath.Create(StringTable, "relative/path").ToString(StringTable)} {AbsolutePath.Create(PathTable, "C:/absolute/path").ToString(PathTable)}",
|
||||
$"execute --test atom {RelativePath.Create(StringTable, "relative/path").ToString(StringTable)} {AbsolutePath.Create(PathTable, path).ToString(PathTable)}",
|
||||
pip.Arguments.ToString(PathTable));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,23 +23,22 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
/// <summary>
|
||||
/// Provides facilities to run the engine adding Rush specific artifacts.
|
||||
/// </summary>
|
||||
[TestClassIfSupported(requiresWindowsBasedOperatingSystem: true)]
|
||||
public abstract class RushIntegrationTestBase : DsTestWithCacheBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Keep in sync with deployment.
|
||||
/// </summary>
|
||||
protected string PathToRush => Path.Combine(TestDeploymentDir, "Rush", "node_modules", "@microsoft", "rush", "bin", "rush").Replace("\\", "/");
|
||||
protected string PathToRush => Path.Combine(TestDeploymentDir, "rush", "node_modules", "@microsoft", "rush", "bin", "rush").Replace("\\", "/");
|
||||
|
||||
/// <summary>
|
||||
/// Keep in sync with deployment.
|
||||
/// </summary>
|
||||
protected string PathToNodeModules => Path.Combine(TestDeploymentDir, "Rush", "node_modules").Replace("\\", "/");
|
||||
protected string PathToNodeModules => Path.Combine(TestDeploymentDir, "rush", "node_modules").Replace("\\", "/");
|
||||
|
||||
/// <summary>
|
||||
/// Keep in sync with deployment.
|
||||
/// </summary>
|
||||
protected string PathToNode => Path.Combine(TestDeploymentDir, "Node", OperatingSystemHelper.IsLinuxOS? "bin/node" : "node.exe").Replace("\\", "/");
|
||||
protected string PathToNode => Path.Combine(TestDeploymentDir, "node", OperatingSystemHelper.IsLinuxOS? "bin/node" : "node.exe").Replace("\\", "/");
|
||||
|
||||
/// <nodoc/>
|
||||
protected string PathToNodeFolder => Path.GetDirectoryName(PathToNode).Replace("\\", "/");
|
||||
|
@ -57,7 +56,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
// By default the engine runs e2e
|
||||
protected virtual EnginePhases Phase => EnginePhases.Execute;
|
||||
|
||||
private string RushUserProfile => Path.Combine(TestRoot, "USERPROFILE").Replace("\\", "/");
|
||||
private string RushUserProfile => Path.Combine(TestRoot, OperatingSystemHelper.IsWindowsOS ? "USERPROFILE" : "HOME").Replace("\\", "/");
|
||||
|
||||
private string RushTempFolder => Path.Combine(TestRoot, "RUSH_TEMP_FOLDER").Replace("\\", "/");
|
||||
|
||||
|
@ -319,7 +318,7 @@ config({{
|
|||
var updatedRushJson = rushJson
|
||||
.Replace(
|
||||
"\"nodeSupportedVersionRange\": \">=12.13.0 <13.0.0 || >=14.15.0 <15.0.0\"",
|
||||
"\"nodeSupportedVersionRange\": \">=10.13.0 <=18.3.0\"")
|
||||
"\"nodeSupportedVersionRange\": \">=10.13.0 <=18.6.0\"")
|
||||
.Replace(
|
||||
"\"pnpmVersion\": \"2.15.1\"",
|
||||
"\"pnpmVersion\": \"5.0.2\"");
|
||||
|
@ -354,9 +353,16 @@ config({{
|
|||
UseShellExecute = false,
|
||||
};
|
||||
|
||||
startInfo.Environment["PATH"] += $";{PathToNodeFolder}";
|
||||
startInfo.Environment["PATH"] += $"{Path.PathSeparator}{PathToNodeFolder}";
|
||||
startInfo.Environment["NODE_PATH"] = PathToNodeModules;
|
||||
startInfo.Environment["USERPROFILE"] = RushUserProfile;
|
||||
if (OperatingSystemHelper.IsWindowsOS)
|
||||
{
|
||||
startInfo.Environment["USERPROFILE"] = RushUserProfile;
|
||||
}
|
||||
else
|
||||
{
|
||||
startInfo.Environment["HOME"] = RushUserProfile;
|
||||
}
|
||||
startInfo.Environment["APPDATA"] = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
startInfo.Environment["RUSH_TEMP_FOLDER"] = RushTempFolder;
|
||||
startInfo.Environment["RUSH_ABSOLUTE_SYMLINKS"] = "1";
|
||||
|
|
|
@ -268,11 +268,11 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
.AddJavaScriptProject("@ms/project-B", "src/B", dependencies: new[] { "@ms/project-A" }, scriptCommands: new[] { ("build", "echo HelloB") })
|
||||
.AddSpec("module.config.dsc", "module({name: 'myModule'});")
|
||||
.AddSpec(@"
|
||||
import {Transformer} from 'Sdk.Transformers';
|
||||
import {Transformer, Artifact, Cmd} from 'Sdk.Transformers';
|
||||
|
||||
const tool : Transformer.ToolDefinition = {
|
||||
exe: Environment.getFileValue('COMSPEC'),
|
||||
dependsOnWindowsDirectories: true
|
||||
exe: Context.isWindowsOS()? Environment.getFileValue('COMSPEC') : f`/usr/bin/bash`,
|
||||
dependsOnCurrentHostOSDirectories: true
|
||||
};
|
||||
|
||||
namespace Test {
|
||||
|
@ -281,7 +281,12 @@ namespace Test {
|
|||
if (project.name === '@ms/project-A') {
|
||||
return Transformer.execute({
|
||||
workingDirectory: project.projectFolder,
|
||||
arguments: [{ value: '/C' }, { value: 'echo HelloA' }, { value: '>' }, { value: 'file.txt' }],
|
||||
arguments: [
|
||||
Cmd.argument(Context.isWindowsOS() ? ""/C"" : ""-c""),
|
||||
Cmd.rawArgument('""'),
|
||||
Cmd.args([""echo"", ""HelloA"", "">"", ""file.txt""]),
|
||||
Cmd.rawArgument('""')
|
||||
],
|
||||
tool: tool,
|
||||
outputs: project.outputs.filter(output => typeof output !== 'Path').map(output => <Transformer.DirectoryOutput>{ directory: output, kind: 'shared' })
|
||||
});
|
||||
|
|
|
@ -22,7 +22,6 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
/// Base class for tests that programmatically add projects and verify the corresponding scheduled process
|
||||
/// done by <see cref="RushResolver"/>
|
||||
/// </summary>
|
||||
[TestClassIfSupported(requiresWindowsBasedOperatingSystem: true)]
|
||||
public abstract class RushPipSchedulingTestBase : PipSchedulingTestBase<JavaScriptProject, RushResolverSettings>
|
||||
{
|
||||
private AbsolutePath m_commonTempFolder = AbsolutePath.Invalid;
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Test.Rush {
|
|||
const rushlib = Node.runNpmPackageInstall(rushLibTest, [], {name: "@microsoft/rush-lib", version: "5.47.0"});
|
||||
|
||||
// TODO: to enable this, we should use an older version of NodeJs for Linux
|
||||
const isRunningOnSupportedSystem = Context.getCurrentHost().cpuArchitecture === "x64" && !BuildXLSdk.isHostOsLinux;
|
||||
const isRunningOnSupportedSystem = Context.getCurrentHost().cpuArchitecture === "x64";
|
||||
|
||||
// TODO: enable Rush tests for non-internal builds when we address the perf issue that make them timeout
|
||||
@@public
|
||||
|
@ -92,7 +92,7 @@ namespace Test.Rush {
|
|||
},
|
||||
{
|
||||
subfolder: a`node`,
|
||||
contents: [Node.nodeExecutables]
|
||||
contents: [Node.nodePackage]
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
@ -23,7 +23,6 @@ namespace Test.BuildXL.FrontEnd.Yarn
|
|||
/// <summary>
|
||||
/// Provides facilities to run the engine adding Yarn specific artifacts.
|
||||
/// </summary>
|
||||
[TestClassIfSupported(requiresWindowsBasedOperatingSystem: true)]
|
||||
public abstract class CustomJavaScriptIntegrationTestBase : DsTestWithCacheBase
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Linq;
|
|||
using BuildXL.Utilities.Configuration;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Test.BuildXL.TestUtilities.Xunit;
|
||||
using Test.BuildXL.TestUtilities.XUnit;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
|
@ -130,7 +131,7 @@ namespace Test.BuildXL.FrontEnd.Yarn.IntegrationTests
|
|||
[InlineData(null, true)]
|
||||
[InlineData("this is not JSON", true)]
|
||||
[InlineData("{'' : {location: 'a/path', workspaceDependencies: []}}", true)]
|
||||
[InlineData("{'project1' : {location: 'invalid:path', workspaceDependencies: []}}", true)]
|
||||
[InlineDataIfSupported(requiresWindowsBasedOperatingSystem: true, data: new object[] { "{'project1' : {location: 'invalid:path', workspaceDependencies: []}}", true})]
|
||||
[InlineData("{'project2' : {location: 'a/path', workspaceDependencies: null}}", true)]
|
||||
[InlineData("{'project3' : {location: 'a/path', workspaceDependencies: ['non-existent-proj']}}", false)]
|
||||
public void CustomGraphFileErrorHandling(string fileContent, bool producesCustomProjectGraphError)
|
||||
|
@ -211,8 +212,8 @@ namespace Test.BuildXL.FrontEnd.Yarn.IntegrationTests
|
|||
|
||||
[Theory]
|
||||
[InlineData("LogsDirectory", true)]
|
||||
[InlineData("ProgramFiles", true)]
|
||||
[InlineData("UserDefinedMount", false)]
|
||||
[InlineDataIfSupported(requiresWindowsBasedOperatingSystem: true, data: new object[] {"ProgramFiles", true})]
|
||||
[InlineDataIfSupported(requiresWindowsBasedOperatingSystem: true, data: new object[] {"UserDefinedMount", false})]
|
||||
public void MountsAreAccesibleDuringCustomScriptEvaluation(string mountName, bool expectSuccess)
|
||||
{
|
||||
string customGraph = @"Map.empty<string, {location: RelativePath, workspaceDependencies: string[]}>()
|
||||
|
|
|
@ -23,18 +23,17 @@ namespace Test.BuildXL.FrontEnd.Yarn
|
|||
/// <summary>
|
||||
/// Provides facilities to run the engine adding Yarn specific artifacts.
|
||||
/// </summary>
|
||||
[TestClassIfSupported(requiresWindowsBasedOperatingSystem: true)]
|
||||
public abstract class YarnIntegrationTestBase : DsTestWithCacheBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Keep in sync with deployment.
|
||||
/// </summary>
|
||||
protected string PathToYarn => Path.Combine(TestDeploymentDir, "yarn", "bin", "yarn").Replace("\\", "/");
|
||||
protected string PathToYarn => Path.Combine(TestDeploymentDir, "yarn", "bin", OperatingSystemHelper.IsWindowsOS ? "yarn.cmd" : "yarn").Replace("\\", "/");
|
||||
|
||||
/// <summary>
|
||||
/// Keep in sync with deployment.
|
||||
/// </summary>
|
||||
protected string PathToNode => Path.Combine(TestDeploymentDir, "Node", OperatingSystemHelper.IsLinuxOS? "bin/node" : "node.exe").Replace("\\", "/");
|
||||
protected string PathToNode => Path.Combine(TestDeploymentDir, "node", OperatingSystemHelper.IsWindowsOS? "node.exe" : "node").Replace("\\", "/");
|
||||
|
||||
/// <nodoc/>
|
||||
protected string PathToNodeFolder => Path.GetDirectoryName(PathToNode).Replace("\\", "/");
|
||||
|
@ -78,7 +77,7 @@ namespace Test.BuildXL.FrontEnd.Yarn
|
|||
bool? enableFullReparsePointResolving = null,
|
||||
string nodeLocation = "")
|
||||
{
|
||||
environment ??= new Dictionary<string, string> {
|
||||
environment ??= new Dictionary<string, string> {
|
||||
["PATH"] = PathToNodeFolder,
|
||||
};
|
||||
|
||||
|
@ -106,6 +105,12 @@ namespace Test.BuildXL.FrontEnd.Yarn
|
|||
["PATH"] = new DiscriminatingUnion<string, UnitValue>(PathToNodeFolder),
|
||||
};
|
||||
|
||||
// On Linux/Mac, yarn depends on low level tools like sed, readlink, etc. If the value for path is not configured as a passthrough, inject /usr/bin
|
||||
if (!OperatingSystemHelper.IsWindowsOS && environment.TryGetValue("PATH", out var value) && value.GetValue() is string path)
|
||||
{
|
||||
environment["PATH"] = new DiscriminatingUnion<string, UnitValue>(path + Path.PathSeparator + "/usr/bin");
|
||||
}
|
||||
|
||||
// We reserve the null string for a true undefined.
|
||||
if (yarnLocation == string.Empty)
|
||||
{
|
||||
|
@ -229,7 +234,7 @@ config({{
|
|||
|
||||
private bool YarnRun(string workingDirectory, string yarnArgs)
|
||||
{
|
||||
string arguments = $"{PathToYarn}.js {yarnArgs}";
|
||||
string arguments = $"{Path.Combine(Path.GetDirectoryName(PathToYarn), "yarn")}.js {yarnArgs}";
|
||||
string filename = PathToNode;
|
||||
|
||||
// Unfortunately, capturing standard out/err non-deterministically hangs node.exe on exit when
|
||||
|
|
|
@ -13,7 +13,6 @@ namespace Test.Yarn {
|
|||
// Yarn is not easily available in the hosted machines that run the public build. So excluding these tests for now outside of the internal build
|
||||
const isRunningOnSupportedSystem =
|
||||
Context.getCurrentHost().cpuArchitecture === "x64" &&
|
||||
!BuildXLSdk.isHostOsLinux &&
|
||||
Environment.getFlag("[Sdk.BuildXL]microsoftInternal");
|
||||
|
||||
@@public
|
||||
|
|
|
@ -40,6 +40,8 @@ namespace BuildXL.FrontEnd.Yarn
|
|||
// If the base location was provided at configuration time, we honor it as is
|
||||
string paths;
|
||||
|
||||
var toolNameToFind = OperatingSystemHelper.IsWindowsOS ? new[] { "yarn.cmd" } : new[] { "yarn" };
|
||||
|
||||
if (resolverSettings.YarnLocation != null)
|
||||
{
|
||||
var value = resolverSettings.YarnLocation.GetValue();
|
||||
|
@ -52,7 +54,7 @@ namespace BuildXL.FrontEnd.Yarn
|
|||
else
|
||||
{
|
||||
var pathCollection = ((IReadOnlyList<DirectoryArtifact>) value).Select(dir => dir.Path);
|
||||
if (!FrontEndUtilities.TryFindToolInPath(m_context, m_host, pathCollection, new[] { "yarn", "yarn.cmd" }, out finalYarnLocation))
|
||||
if (!FrontEndUtilities.TryFindToolInPath(m_context, m_host, pathCollection, toolNameToFind, out finalYarnLocation))
|
||||
{
|
||||
failure = $"'yarn' cannot be found under any of the provided paths '{string.Join(Path.PathSeparator.ToString(), pathCollection.Select(path => path.ToString(m_context.PathTable)))}'.";
|
||||
return false;
|
||||
|
@ -66,7 +68,7 @@ namespace BuildXL.FrontEnd.Yarn
|
|||
// If the location was not provided, let's try to see if Yarn is under %PATH%
|
||||
paths = buildParameters["PATH"];
|
||||
|
||||
if (!FrontEndUtilities.TryFindToolInPath(m_context, m_host, paths, new[] { "yarn", "yarn.cmd"}, out finalYarnLocation))
|
||||
if (!FrontEndUtilities.TryFindToolInPath(m_context, m_host, paths, toolNameToFind, out finalYarnLocation))
|
||||
{
|
||||
failure = "A location for 'yarn' is not explicitly specified. However, 'yarn' doesn't seem to be part of PATH. You can either specify the location explicitly using 'yarnLocation' field in " +
|
||||
$"the Yarn resolver configuration, or make sure 'yarn' is part of your PATH. Current PATH is '{paths}'.";
|
||||
|
|
|
@ -59,11 +59,20 @@ try {
|
|||
* data: '{ 'workspaceName' : { location: 'some/location', workspaceDependencies: [], mismatchedWorkspaceDependencies: [] } }'
|
||||
* }
|
||||
*/
|
||||
let workspaceJson = JSON.parse(testJson === undefined
|
||||
? execSync(`"${pathToYarn}" --silent workspaces info --json`).toString()
|
||||
: testJson
|
||||
);
|
||||
|
||||
let workspaceJson;
|
||||
if (testJson !== undefined) {
|
||||
workspaceJson = JSON.parse(testJson);
|
||||
}
|
||||
else {
|
||||
// This yarn execution sometimes non-deterministically makes node non-terminating. Debugging this call shows a dangling pipe
|
||||
// that seems to be related to stdout/stderr piping to the main process. In order to workaround this issue, output the raw
|
||||
// report to the output graph file and immediately read it back for post-processing. The final graph (in the format bxl expects)
|
||||
// will be rewritten into the same file
|
||||
execSync(`"${pathToYarn}" --silent workspaces info --json > "${outputGraphFile}"`, {stdio: "ignore"});
|
||||
|
||||
workspaceJson = JSON.parse(fs.readFileSync(outputGraphFile, "utf8"));
|
||||
}
|
||||
|
||||
// Parse the data key if the old format is found.
|
||||
if ("type" in workspaceJson && workspaceJson["type"] === "log") {
|
||||
workspaceJson = JSON.parse(workspaceJson["data"]);
|
||||
|
@ -103,6 +112,6 @@ try {
|
|||
} catch (Error) {
|
||||
// Standard error from this tool is exposed directly to the user.
|
||||
// Catch any exceptions and just print out the message.
|
||||
console.error(Error.message);
|
||||
console.error(Error.message || Error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
|
@ -15,8 +15,8 @@ namespace Test.Tool.Javascript
|
|||
{
|
||||
public PathTable PathTable;
|
||||
public string UserProfile => Path.Combine(TestDeploymentDir, "userprofile");
|
||||
public string NodeTool => Path.Combine(TestDeploymentDir, "node", OperatingSystemHelper.IsUnixOS ? "node" : "node.exe");
|
||||
public string YarnTool => Path.Combine(TestDeploymentDir, "yarn", "bin", "yarn");
|
||||
public string NodeTool => Path.Combine(TestDeploymentDir, "node", OperatingSystemHelper.IsWindowsOS ? "node.exe" : "node");
|
||||
public string YarnTool => Path.Combine(TestDeploymentDir, "yarn", "bin", OperatingSystemHelper.IsWindowsOS ? "yarn.cmd" : "yarn");
|
||||
|
||||
public JavascriptGraphBuilderTestBase(ITestOutputHelper output) : base(output)
|
||||
{
|
||||
|
@ -42,8 +42,15 @@ namespace Test.Tool.Javascript
|
|||
UseShellExecute = false,
|
||||
};
|
||||
|
||||
startInfo.Environment["PATH"] += $";{Path.GetDirectoryName(NodeTool)}";
|
||||
startInfo.Environment["USERPROFILE"] = UserProfile;
|
||||
// Low level tools like sed and readlink needs to be in the path for the linux/mac case
|
||||
var path = $"{Path.PathSeparator}{Path.GetDirectoryName(NodeTool)}";
|
||||
if (!OperatingSystemHelper.IsWindowsOS)
|
||||
{
|
||||
path += Path.PathSeparator + "/usr/bin/";
|
||||
}
|
||||
|
||||
startInfo.Environment["PATH"] += path;
|
||||
startInfo.Environment[OperatingSystemHelper.IsWindowsOS ? "USERPROFILE" : "HOME"] = UserProfile;
|
||||
|
||||
var graphBuilderResult = Process.Start(startInfo);
|
||||
graphBuilderResult.WaitForExit();
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace Test.Tool.JavascriptGraphBuilder {
|
|||
// Yarn is not easily available in the hosted machines that run the public build. So excluding these tests for now outside of the internal build
|
||||
const isRunningOnSupportedSystem =
|
||||
Context.getCurrentHost().cpuArchitecture === "x64" &&
|
||||
!BuildXLSdk.isHostOsLinux && !BuildXLSdk.isHostOsOsx &&
|
||||
!BuildXLSdk.isHostOsOsx &&
|
||||
Environment.getFlag("[Sdk.BuildXL]microsoftInternal");
|
||||
|
||||
@@public
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BuildXL.FrontEnd.JavaScript.ProjectGraph;
|
||||
|
@ -11,9 +11,9 @@ using Test.BuildXL.TestUtilities.Xunit;
|
|||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Test.Tool.Javascript.YarnGraphBuilder
|
||||
{
|
||||
public class YarnGraphBuilderTests : JavascriptGraphBuilderTestBase
|
||||
namespace Test.Tool.Javascript.YarnGraphBuilder
|
||||
{
|
||||
public class YarnGraphBuilderTests : JavascriptGraphBuilderTestBase
|
||||
{
|
||||
private readonly string m_yarnWorkSpaceRoot;
|
||||
private readonly string m_yarnGraphBuilderToolRoot;
|
||||
|
@ -113,5 +113,5 @@ namespace Test.Tool.Javascript.YarnGraphBuilder
|
|||
XAssert.IsTrue(projects[0].Name.Equals("workspace-a"));
|
||||
XAssert.IsTrue(projects[0].ProjectFolder.ToString(PathTable).Equals(Path.Combine(m_yarnWorkSpaceRoot, "workspace-a")));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Test.BuildXL.TestUtilities.Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Test.BuildXL.TestUtilities.XUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom inline data attribute that allows dynamically skipping tests based on what operations are supported
|
||||
/// </summary>
|
||||
[TraitDiscoverer(AdminTestDiscoverer.ClassName, AdminTestDiscoverer.AssemblyName)]
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
public sealed class InlineDataIfSupported : DataAttribute, ITraitAttribute
|
||||
{
|
||||
private readonly object[] m_data;
|
||||
private readonly ITestIfSupportedTraitAttribute m_inner;
|
||||
|
||||
/// <inheritdoc />
|
||||
public TestRequirements Requirements => m_inner.Requirements;
|
||||
|
||||
/// <nodoc/>
|
||||
public InlineDataIfSupported(
|
||||
bool requiresAdmin = false,
|
||||
bool requiresJournalScan = false,
|
||||
bool requiresSymlinkPermission = false,
|
||||
bool requiresWindowsBasedOperatingSystem = false,
|
||||
bool requiresUnixBasedOperatingSystem = false,
|
||||
bool requiresHeliumDriversAvailable = false,
|
||||
bool requiresMacOperatingSystem = false,
|
||||
bool requiresWindowsOrMacOperatingSystem = false,
|
||||
bool requiresWindowsOrLinuxOperatingSystem = false,
|
||||
TestRequirements additionalRequirements = TestRequirements.None,
|
||||
params object[] data)
|
||||
{
|
||||
m_data = data;
|
||||
|
||||
m_inner = new FactIfSupportedAttribute(
|
||||
requiresAdmin: requiresAdmin,
|
||||
requiresJournalScan: requiresJournalScan,
|
||||
requiresSymlinkPermission: requiresSymlinkPermission,
|
||||
requiresWindowsBasedOperatingSystem: requiresWindowsBasedOperatingSystem,
|
||||
requiresUnixBasedOperatingSystem: requiresUnixBasedOperatingSystem,
|
||||
requiresHeliumDriversAvailable: requiresHeliumDriversAvailable,
|
||||
requiresMacOperatingSystem: requiresMacOperatingSystem,
|
||||
requiresWindowsOrMacOperatingSystem: requiresWindowsOrMacOperatingSystem,
|
||||
additionalRequirements: additionalRequirements,
|
||||
requiresWindowsOrLinuxOperatingSystem: requiresWindowsOrLinuxOperatingSystem
|
||||
);
|
||||
|
||||
Skip = m_inner.Skip;
|
||||
}
|
||||
|
||||
/// <nodoc/>
|
||||
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
|
||||
{
|
||||
return new object[1][] { m_data };
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2710,15 +2710,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Component": {
|
||||
"Type": "NuGet",
|
||||
"NuGet": {
|
||||
"Name": "NPM.OnCloudbuild",
|
||||
"Version": "3.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Component": {
|
||||
"Type": "NuGet",
|
||||
|
|
20
config.dsc
20
config.dsc
|
@ -542,22 +542,28 @@ config({
|
|||
// NodeJs
|
||||
{
|
||||
moduleName: "NodeJs.win-x64",
|
||||
url: "https://nodejs.org/dist/v18.3.0/node-v18.3.0-win-x64.zip",
|
||||
hash: "VSO0:AEABD74C344D3DE10CB5CD0E2FE49E6210E066F4EB982A86627B8E54156F3C8200",
|
||||
url: "https://nodejs.org/dist/v18.6.0/node-v18.6.0-win-x64.zip",
|
||||
hash: "VSO0:EA729EEA528055396523F3F5BD61EDD769C251EB7B4483AABFEB511333E60AA000",
|
||||
archiveType: "zip",
|
||||
},
|
||||
{
|
||||
moduleName: "NodeJs.osx-x64",
|
||||
url: "https://nodejs.org/dist/v18.3.0/node-v18.3.0-darwin-x64.tar.gz",
|
||||
hash: "VSO0:0E2D521D3E250F3969D529303E15D260D6E465FADC2DEA20215DED422CAE2EC600",
|
||||
url: "https://nodejs.org/dist/v18.6.0/node-v18.6.0-darwin-x64.tar.gz",
|
||||
hash: "VSO0:653B5954AD06BB6C9B7141853649602790FCB0031B81FDB82241333E2EE1350200",
|
||||
archiveType: "tgz",
|
||||
},
|
||||
{
|
||||
moduleName: "NodeJs.linux-x64",
|
||||
url: "https://nodejs.org/dist/v18.3.0/node-v18.3.0-linux-x64.tar.gz",
|
||||
hash: "VSO0:6CD39D92AC462BC97A2B445A7A1A3ACB5A3851AEF9C63AC7CC9F067DD067D38300",
|
||||
url: "https://nodejs.org/dist/v18.6.0/node-v18.6.0-linux-x64.tar.gz",
|
||||
hash: "VSO0:15A59CD4CC7C08A91FDF0C028F1C1129DC4B635749514739E1B2C6224E6420FB00",
|
||||
archiveType: "tgz",
|
||||
}
|
||||
},
|
||||
{
|
||||
moduleName: "YarnTool",
|
||||
extractedValueName: "yarnPackage",
|
||||
url: 'https://registry.npmjs.org/yarn/-/yarn-1.22.19.tgz',
|
||||
archiveType: "tgz"
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -61,9 +61,6 @@ export const pkgs = isMicrosoftInternal ? [
|
|||
// Internal pacakged version to avoid downloading from the web but the trusted stable internal feed:
|
||||
{ id: "PowerShell.Core", version: "6.1.0", osSkip: [ "macOS", "unix" ] },
|
||||
|
||||
// Officially mantained CB package that contains Yarn. Used for Yarn tests.
|
||||
{ id: "NPM.OnCloudbuild", version: "3.1.0" },
|
||||
|
||||
// IcM and dependencies
|
||||
{ id: "Microsoft.AzureAd.Icm.Types.amd64", version: "2.2.1363.11" },
|
||||
{ id: "Microsoft.AzureAd.Icm.WebService.Client.amd64", version: "2.2.1363.11" },
|
||||
|
|
Загрузка…
Ссылка в новой задаче