Example #1
0
        public PipDataTests(ITestOutputHelper output)
            : base(output)
        {
            m_pathTable         = new PathTable();
            m_expectedStringId0 = StringId.Create(m_pathTable.StringTable, ExpectedString0);
            m_pipDataBuilder    = new PipDataBuilder(m_pathTable.StringTable);

            m_expectedStringId0 = StringId.Create(m_pathTable.StringTable, ExpectedString0);
            m_uniqueEntry0      = AbsolutePath.Create(m_pathTable, A("c", "unique to fragment 0"));

            // BEGIN ADDING ARGUMENTS
            m_cursorStart = m_pipDataBuilder.CreateCursor();

            AddStandardBlock(m_pipDataBuilder);

            m_cursor0 = m_pipDataBuilder.CreateCursor();

            using (m_pipDataBuilder.StartFragment(Escaping0, m_separator0))
            {
                m_pipDataBuilder.Add(m_uniqueEntry0);
                AddStandardBlock(m_pipDataBuilder);
            }

            m_cursor1 = m_pipDataBuilder.CreateCursor();

            using (m_pipDataBuilder.StartFragment(Escaping1, Separator1))
            {
                AddStandardBlock(m_pipDataBuilder);
                m_pipDataBuilder.Add(UniqueEntry1);
            }

            m_cursorEnd = m_pipDataBuilder.CreateCursor();

            // END ADDING ARGUMENTS
        }
Example #2
0
 private static void AddLogArgument(PipDataBuilder pipDataBuilder, int loggerNumber, AbsolutePath logFile, string verbosity)
 {
     using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.NoEscaping, string.Empty))
     {
         pipDataBuilder.Add(PipDataAtom.FromString(I($"/flp{loggerNumber}:logfile=")));
         pipDataBuilder.Add((PipDataAtom.FromAbsolutePath(logFile)));
         pipDataBuilder.Add(PipDataAtom.FromString(I($";{verbosity}")));
     }
 }
Example #3
0
 private static void AddMsBuildProperty(PipDataBuilder pipDataBuilder, string key, string value)
 {
     // Make sure properties are always quoted, since MSBuild doesn't handle semicolon separated
     // properties without quotes
     using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.NoEscaping, string.Empty))
     {
         pipDataBuilder.Add(PipDataAtom.FromString("/p:"));
         using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, string.Empty))
         {
             pipDataBuilder.Add(PipDataAtom.FromString(key));
         }
         pipDataBuilder.Add(PipDataAtom.FromString("=\""));
         using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, string.Empty))
         {
             pipDataBuilder.Add(PipDataAtom.FromString(value));
         }
         pipDataBuilder.Add(PipDataAtom.FromString("\""));
     }
 }
Example #4
0
        private void AddOption <TValue>(string optionName, TValue value, Action <PipDataBuilder, TValue> writeValue)
        {
            Contract.Requires(!string.IsNullOrEmpty(optionName));
            Contract.Requires(writeValue != null);
            Contract.Assert(!m_finished);

            using (m_builder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, string.Empty))
            {
                m_builder.Add(optionName);
                writeValue(m_builder, value);
            }
        }
        private void AddOption <TValue>(string prefix, TValue value, bool valueIsEmpty, Action <PipDataBuilder, TValue> writeValue)
        {
            // prefix and value are null -> skip
            if (string.IsNullOrEmpty(prefix) && valueIsEmpty)
            {
                return;
            }

            if (string.IsNullOrEmpty(prefix))
            {
                // This is unnamed argument
                writeValue(ArgumentsBuilder, value);
                return;
            }

            if (valueIsEmpty)
            {
                // This is a flag (or switch) kind of arguments
                ArgumentsBuilder.Add(prefix);
                return;
            }

            // both prefix and value are non-empty
            //   - handle the special case when prefix ends with space
            //       -> (1) add trimmed prefix, (2) add raw space, (3) add value
            if (prefix.EndsWith(" ", StringComparison.OrdinalIgnoreCase))
            {
                AddRawText(prefix.TrimEnd(' '));
                writeValue(ArgumentsBuilder, value);
                return;
            }

            using (ArgumentsBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, EmptyStringId))
            {
                ArgumentsBuilder.Add(prefix);
                writeValue(ArgumentsBuilder, value);
            }
        }
        private void AddJavaScriptArgumentToBuilder(PipDataBuilder argumentsBuilder, JavaScriptArgument value)
        {
            switch (value.GetValue())
            {
            case string s:
                using (argumentsBuilder.StartFragment(PipDataFragmentEscaping.NoEscaping, m_context.StringTable.Empty))
                {
                    argumentsBuilder.Add(s);
                }
                break;

            case AbsolutePath absolutePath:
                using (argumentsBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, m_context.StringTable.Empty))
                {
                    argumentsBuilder.Add(absolutePath);
                }
                break;

            case RelativePath relativePath:
                using (argumentsBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, m_context.StringTable.Empty))
                {
                    argumentsBuilder.Add(relativePath);
                }
                break;

            case PathAtom pathAtom:
                using (argumentsBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, m_context.StringTable.Empty))
                {
                    argumentsBuilder.Add(pathAtom);
                }
                break;

            default:
                Contract.Assert(false, $"Unexpected argument '{value.GetType()}'");
                break;
            }
        }
Example #7
0
        public static void Add(this PipDataBuilder @this, string prefix, RelativePath value)
        {
            var escaping  = PipDataFragmentEscaping.CRuntimeArgumentRules;
            var separator = System.IO.Path.DirectorySeparatorChar.ToString();

            using (@this.StartFragment(escaping, separator))
            {
                if (!string.IsNullOrEmpty(prefix))
                {
                    @this.Add(prefix);
                }

                foreach (var atom in value.GetAtoms())
                {
                    @this.Add(atom);
                }
            }
        }
Example #8
0
        private static Process CreateConsoleProcessInContainer(
            BuildXLContext context,
            TempFileStorage tempFiles,
            PathTable pt,
            string arguments,
            ReadOnlyArray <FileArtifactWithAttributes> outputFiles,
            ReadOnlyArray <DirectoryArtifact> directoryOutputs,
            ContainerIsolationLevel containerIsolationLevel = ContainerIsolationLevel.IsolateAllOutputs)
        {
            var executableFileArtifact = FileArtifact.CreateSourceFile(AbsolutePath.Create(context.PathTable, CmdHelper.CmdX64));

            var argumentBuilder = new PipDataBuilder(context.PathTable.StringTable);

            argumentBuilder.Add("/d");
            argumentBuilder.Add("/c");
            using (argumentBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, " "))
            {
                foreach (var arg in arguments.Split(new[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries))
                {
                    argumentBuilder.Add(arg);
                }
            }

            string workingDirectory             = tempFiles.GetUniqueDirectory();
            var    workingDirectoryAbsolutePath = AbsolutePath.Create(context.PathTable, workingDirectory);

            string uniqueOutputDirectory     = tempFiles.GetUniqueDirectory();
            var    uniqueOutputDirectoryPath = AbsolutePath.Create(context.PathTable, uniqueOutputDirectory);

            string uniqueRedirectedOutputDirectory     = tempFiles.GetUniqueDirectory("redirected");
            var    uniqueRedirectedOutputDirectoryPath = AbsolutePath.Create(context.PathTable, uniqueRedirectedOutputDirectory);

            var pip = new Process(
                executableFileArtifact,
                workingDirectoryAbsolutePath,
                argumentBuilder.ToPipData(" ", PipDataFragmentEscaping.NoEscaping),
                FileArtifact.Invalid,
                PipData.Invalid,
                ReadOnlyArray <EnvironmentVariable> .FromWithoutCopy(),
                FileArtifact.Invalid,
                FileArtifact.Invalid,
                FileArtifact.Invalid,
                tempFiles.GetUniqueDirectory(pt),
                null,
                null,
                dependencies: ReadOnlyArray <FileArtifact> .FromWithoutCopy(new[] { executableFileArtifact }),
                outputs: outputFiles,
                directoryDependencies: ReadOnlyArray <DirectoryArtifact> .Empty,
                directoryOutputs: directoryOutputs,
                orderDependencies: ReadOnlyArray <PipId> .Empty,
                untrackedPaths: ReadOnlyArray <AbsolutePath> .Empty,
                untrackedScopes: ReadOnlyArray <AbsolutePath> .Empty,
                tags: ReadOnlyArray <StringId> .Empty,
                successExitCodes: ReadOnlyArray <int> .Empty,
                semaphores: ReadOnlyArray <ProcessSemaphoreInfo> .Empty,
                provenance: PipProvenance.CreateDummy(context),
                toolDescription: StringId.Invalid,
                additionalTempDirectories: ReadOnlyArray <AbsolutePath> .Empty,
                options: Process.Options.NeedsToRunInContainer,
                uniqueOutputDirectory: uniqueOutputDirectoryPath,
                uniqueRedirectedDirectoryRoot: uniqueRedirectedOutputDirectoryPath,
                containerIsolationLevel: containerIsolationLevel);

            return(pip);
        }
Example #9
0
        private bool TryAddMsBuildArguments(ProjectWithPredictions project, PipDataBuilder pipDataBuilder, AbsolutePath logDirectory, AbsolutePath outputResultCacheFile, out string failureDetail)
        {
            // Common arguments to all MsBuildExe invocations
            pipDataBuilder.AddRange(s_commonArgumentsToMsBuildExe.Select(argument => PipDataAtom.FromString(argument)));

            // Log verbosity
            if (!TryGetLogVerbosity(m_resolverSettings.LogVerbosity, out string logVerbosity))
            {
                failureDetail = $"Cannot set the MSBuild log verbosity. '{m_resolverSettings.LogVerbosity}' is not a valid option.";
                return(false);
            }

            AddLogArgument(pipDataBuilder, 1, logDirectory.Combine(PathTable, "msbuild.log"), $"Verbosity={logVerbosity}");
            AddLogArgument(pipDataBuilder, 2, logDirectory.Combine(PathTable, "msbuild.wrn"), "Verbosity=Quiet;warningsonly");
            AddLogArgument(pipDataBuilder, 3, logDirectory.Combine(PathTable, "msbuild.err"), "Verbosity=Quiet;errorsonly");
            AddLogArgument(pipDataBuilder, 4, logDirectory.Combine(PathTable, "msbuild.prf"), "PerformanceSummary");

            // Global properties on the project are turned into build parameters
            foreach (var kvp in project.GlobalProperties)
            {
                AddMsBuildProperty(pipDataBuilder, kvp.Key, kvp.Value);
            }

            // Configure binary logger if specified
            if (m_resolverSettings.EnableBinLogTracing == true)
            {
                using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.NoEscaping, string.Empty))
                {
                    pipDataBuilder.Add(PipDataAtom.FromString("/binaryLogger:"));
                    pipDataBuilder.Add(PipDataAtom.FromAbsolutePath(logDirectory.Combine(PathTable, "msbuild.binlog")));
                }
            }

            // Targets to execute.
            var targets = project.PredictedTargetsToExecute.Targets;

            Contract.Assert(targets.Count > 0);
            foreach (string target in targets)
            {
                pipDataBuilder.Add(PipDataAtom.FromString($"/t:{target}"));
            }


            // Pass the output result cache file if present
            if (outputResultCacheFile != AbsolutePath.Invalid)
            {
                using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.NoEscaping, string.Empty))
                {
                    // Flag /orc is the short form of /outputResultsCache, and part of MSBuild 'build in isolation' mode.
                    // By specifying this flag, MSBuild will write the build result at the end of this invocation into the cache file
                    pipDataBuilder.Add(PipDataAtom.FromString("/orc:"));
                    pipDataBuilder.Add(PipDataAtom.FromAbsolutePath(outputResultCacheFile));
                }
            }
            else
            {
                // In legacy (non-isolated) mode, we still have to rely on SDKs honoring this flag
                pipDataBuilder.Add(PipDataAtom.FromString("/p:buildprojectreferences=false"));
            }

            failureDetail = string.Empty;
            return(true);
        }
Example #10
0
        private bool TryAddMsBuildArguments(ProjectWithPredictions project, PipDataBuilder pipDataBuilder, AbsolutePath logDirectory, AbsolutePath outputResultCacheFile, out string failureDetail)
        {
            // Common arguments to all MsBuildExe invocations
            pipDataBuilder.AddRange(s_commonArgumentsToMsBuildExe.Select(argument => PipDataAtom.FromString(argument)));

            // Log verbosity
            if (!TryGetLogVerbosity(m_resolverSettings.LogVerbosity, out string logVerbosity))
            {
                failureDetail = $"Cannot set the MSBuild log verbosity. '{m_resolverSettings.LogVerbosity}' is not a valid option.";
                return(false);
            }

            AddLogArgument(pipDataBuilder, 1, logDirectory.Combine(PathTable, "msbuild.log"), $"Verbosity={logVerbosity}");
            AddLogArgument(pipDataBuilder, 2, logDirectory.Combine(PathTable, "msbuild.wrn"), "Verbosity=Quiet;warningsonly");
            AddLogArgument(pipDataBuilder, 3, logDirectory.Combine(PathTable, "msbuild.err"), "Verbosity=Quiet;errorsonly");
            AddLogArgument(pipDataBuilder, 4, logDirectory.Combine(PathTable, "msbuild.prf"), "PerformanceSummary");

            // Global properties on the project are turned into build parameters
            foreach (var kvp in project.GlobalProperties)
            {
                AddMsBuildProperty(pipDataBuilder, kvp.Key, kvp.Value);
            }

            // Configure binary logger if specified
            if (m_resolverSettings.EnableBinLogTracing == true)
            {
                using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.NoEscaping, string.Empty))
                {
                    pipDataBuilder.Add(PipDataAtom.FromString("/binaryLogger:"));
                    pipDataBuilder.Add(PipDataAtom.FromAbsolutePath(logDirectory.Combine(PathTable, "msbuild.binlog")));
                }
            }

            // Targets to execute.
            // If the prediction is available, there should be at least one target (otherwise it makes no sense to schedule the project, and we should have caught this earlier)
            // If the prediction is not available, we fallback to call default targets (which means not passing any specific /t:). A more strict policy would be to bail out
            // here saying that the project is not complying to the target protocol specification. We leave it relaxed for now, but we log it.
            // https://github.com/Microsoft/msbuild/blob/master/documentation/specs/static-graph.md
            if (project.PredictedTargetsToExecute.IsPredictionAvailable)
            {
                var targets = project.PredictedTargetsToExecute.Targets;
                Contract.Assert(targets.Count > 0);
                foreach (string target in targets)
                {
                    pipDataBuilder.Add(PipDataAtom.FromString($"/t:{target}"));
                }
            }
            else
            {
                // The prediction for the targets to execute is not available. Just log this as a warning for now, defaults targets will be used.
                Tracing.Logger.Log.ProjectIsNotSpecifyingTheProjectReferenceProtocol(
                    m_context.LoggingContext,
                    Location.FromFile(project.FullPath.ToString(PathTable)),
                    project.FullPath.GetName(m_context.PathTable).ToString(m_context.StringTable));
            }

            // Pass the output result cache file if present
            if (outputResultCacheFile != AbsolutePath.Invalid)
            {
                using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.NoEscaping, string.Empty))
                {
                    // Flag /orc is the short form of /outputResultsCache, and part of MSBuild 'build in isolation' mode.
                    // By specifying this flag, MSBuild will write the build result at the end of this invocation into the cache file
                    pipDataBuilder.Add(PipDataAtom.FromString("/orc:"));
                    pipDataBuilder.Add(PipDataAtom.FromAbsolutePath(outputResultCacheFile));
                }
            }
            else
            {
                // In legacy (non-isolated) mode, we still have to rely on SDKs honoring this flag
                pipDataBuilder.Add(PipDataAtom.FromString("/p:buildprojectreferences=false"));
            }

            failureDetail = string.Empty;
            return(true);
        }
Example #11
0
        /// <summary>
        /// Creates arguments.
        /// </summary>
        internal static PipData CreateCmdArguments(
            StringTable stringTable,
            IEnumerable <FileArtifact> dependencies,
            IEnumerable <FileArtifact> outputs,
            bool includeWarning = false)
        {
            Contract.Requires(dependencies != null, "Argument dependencies cannot be null");
            Contract.Requires(outputs != null, "Argument outputs cannot be null");

            var pipDataBuilder = new PipDataBuilder(stringTable);
            int i = 0;

            pipDataBuilder.Add("/d");
            pipDataBuilder.Add("/c");

            foreach (FileArtifact output in outputs)
            {
                if (i > 0)
                {
                    pipDataBuilder.Add("&");
                }

                using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, " "))
                {
                    var allDependencies = dependencies.ToList();

                    pipDataBuilder.Add("type");
                    foreach (FileArtifact dependency in allDependencies)
                    {
                        pipDataBuilder.Add(dependency);
                    }

                    pipDataBuilder.Add(">");
                    pipDataBuilder.Add(output);
                }

                pipDataBuilder.Add("&");

                using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, " "))
                {
                    pipDataBuilder.Add("echo");
                    pipDataBuilder.Add("buildxl");
                    pipDataBuilder.Add(">>");
                    pipDataBuilder.Add(output);
                }

                i++;
            }

            if (includeWarning)
            {
                if (outputs.Any())
                {
                    pipDataBuilder.Add("&&");
                }

                using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, " "))
                {
                    pipDataBuilder.Add("echo");
                    pipDataBuilder.Add(WarningRegexDescription);
                }
            }

            return(pipDataBuilder.ToPipData(" ", PipDataFragmentEscaping.CRuntimeArgumentRules));
        }
Example #12
0
        private bool TryAddMsBuildArguments(ProjectWithPredictions <AbsolutePath> project, Qualifier qualifier, PipDataBuilder pipDataBuilder, AbsolutePath logDirectory, out string failureDetail)
        {
            // Common arguments to all MsBuildExe invocations
            pipDataBuilder.AddRange(s_commonArgumentsToMsBuildExe.Select(argument => PipDataAtom.FromString(argument)));

            // Log verbosity
            if (!TryGetLogVerbosity(m_resolverSettings.LogVerbosity, out string logVerbosity))
            {
                failureDetail = $"Cannot set the MSBuild log verbosity. '{m_resolverSettings.LogVerbosity}' is not a valid option.";
                return(false);
            }

            AddLogArgument(pipDataBuilder, 1, logDirectory.Combine(PathTable, "msbuild.log"), $"Verbosity={logVerbosity}");
            AddLogArgument(pipDataBuilder, 2, logDirectory.Combine(PathTable, "msbuild.wrn"), "Verbosity=Quiet;warningsonly");
            AddLogArgument(pipDataBuilder, 3, logDirectory.Combine(PathTable, "msbuild.err"), "Verbosity=Quiet;errorsonly");
            AddLogArgument(pipDataBuilder, 4, logDirectory.Combine(PathTable, "msbuild.prf"), "PerformanceSummary");

            // Global properties on the project are turned into build parameters
            foreach (var kvp in project.GlobalProperties)
            {
                AddMsBuildProperty(pipDataBuilder, kvp.Key, kvp.Value);
            }

            // The specified qualifier, unless overridden by a global property, is turned into a property as well
            // This means that:
            // 1) if the project is not being referenced with a specific property that matters to the qualifier, the requested qualifier is used
            // 2) if a particular property (e.g. platform) is set when referencing the project, that is honored
            foreach (StringId key in qualifier.Keys)
            {
                string keyAsString = key.ToString(m_context.StringTable);
                if (!project.GlobalProperties.ContainsKey(keyAsString))
                {
                    var success = qualifier.TryGetValue(m_context.StringTable, keyAsString, out string value);
                    Contract.Assert(success);
                    AddMsBuildProperty(pipDataBuilder, keyAsString, value);
                }
            }

            // Configure binary logger if specified
            if (m_resolverSettings?.EnableBinLogTracing == true)
            {
                using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.NoEscaping, string.Empty))
                {
                    pipDataBuilder.Add(PipDataAtom.FromString("/binaryLogger:"));
                    pipDataBuilder.Add(PipDataAtom.FromAbsolutePath(logDirectory.Combine(PathTable, "msbuild.binlog")));
                }
            }

            // Targets to execute.
            // If the prediction is available, there should be at least one target (otherwise it makes no sense to schedule the project, and we should have caught this earlier)
            // If the prediction is not available, we fallback to call default targets (which means not passing any specific /t:). A more strict policy would be to bail out
            // here saying that the project is not complying to the target protocol specification. We leave it relaxed for now, but we log it.
            // https://github.com/Microsoft/msbuild/blob/master/documentation/specs/static-graph.md
            if (project.PredictedTargetsToExecute.IsPredictionAvailable)
            {
                var targets = project.PredictedTargetsToExecute.Targets;
                Contract.Assert(targets.Count > 0);
                foreach (string target in targets)
                {
                    pipDataBuilder.Add(PipDataAtom.FromString($"/t:{target}"));
                }
            }
            else
            {
                // The prediction for the targets to execute is not available. Just log this as a warning for now, defaults targets will be used.
                Tracing.Logger.Log.ProjectIsNotSpecifyingTheProjectReferenceProtocol(
                    m_context.LoggingContext,
                    Location.FromFile(project.FullPath.ToString(PathTable)),
                    project.FullPath.GetName(m_context.PathTable).ToString(m_context.StringTable));
            }

            failureDetail = string.Empty;
            return(true);
        }