Example #1
0
        /// <summary>
        /// Get all the environment exposed to the process, with the values overriden by the engine
        /// </summary>
        public static IDictionary <string, string> GetEngineEnvironment(FrontEndEngineAbstraction engine, string frontEndName)
        {
            var         engineEnvironment = new Dictionary <string, string>();
            IDictionary environment       = Environment.GetEnvironmentVariables();

            foreach (string environmentVariable in environment.Keys)
            {
                // Expose as much of the environment as we can -- use the ones overriden by the Engine
                if (engine.TryGetBuildParameter(environmentVariable, frontEndName, out var value))
                {
                    engineEnvironment[environmentVariable] = value;
                }
            }

            return(engineEnvironment);
        }
Example #2
0
        /// <summary>
        /// Retrieves a list of search locations for an executable, inspecting a list of explicit candidates first or using PATH.
        /// The onEmptyResult action will be invoked if there are no search locations available
        /// The onPathParseFailure action will be invoked with the PATH as an argument if the PATH is malformed
        /// </summary>
        public static bool TryRetrieveExecutableSearchLocations(
            string frontEnd,
            FrontEndContext context,
            FrontEndEngineAbstraction engine,
            IReadOnlyCollection <AbsolutePath> explicitCandidates,
            out IEnumerable <AbsolutePath> searchLocations,
            Action onEmptyResult = null,
            Action <string> onPathParseFailure = null)
        {
            // If there are explicit search locations specified, use those
            if (explicitCandidates?.Count > 0)
            {
                searchLocations = explicitCandidates;
                return(true);
            }

            // Otherwise use %PATH%
            if (!engine.TryGetBuildParameter("PATH", frontEnd, out string paths))
            {
                onEmptyResult?.Invoke();
                searchLocations = null;
                return(false);
            }

            var locations = new List <AbsolutePath>();

            foreach (string path in paths.Split(new[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries))
            {
                var nonEscapedPath = path.Trim('"');
                if (AbsolutePath.TryCreate(context.PathTable, nonEscapedPath, out var absolutePath))
                {
                    locations.Add(absolutePath);
                }
            }

            if (locations.Count == 0)
            {
                onPathParseFailure?.Invoke(paths);
                searchLocations = null;
                return(false);
            }

            searchLocations = locations;
            return(true);
        }
        /// <summary>
        /// Expands environment variables in a string by querying the engine for the corresponding build parameters
        /// </summary>
        /// <remarks>
        /// Even though there is a Environment.ExpandEnvironmentVariables(String) available in C# this function only works
        /// for Windows (https://github.com/dotnet/runtime/issues/25792). Furthermore, we need the expansion to consider
        /// the configured build parameters in addition to the actual environment, and to properly register the access to those parameters in order
        /// for incrementality to safely work.
        /// </remarks>
        /// <returns>Whether there is at least one environment variable in the string that was expanded</returns>
        public static bool TryExpandVariablesInString(string stringToExpand, FrontEndEngineAbstraction engine, out string expandedString)
        {
            // if the path does not contain an env variable, just return the original path to avoid extra allocations
#if NETCOREAPP
            if (stringToExpand?.Contains(s_variableSeparator) != true)
#else
            if (stringToExpand?.Contains(s_variableSeparator.ToString()) != true)
#endif
            {
                expandedString = stringToExpand;
                return(false);
            }

            // Here we build the result, containing all variables expanded
            StringBuilder result = new StringBuilder(stringToExpand.Length);
            // Here we build the variable name we'll query the environment with. Reused across each variable
            StringBuilder variableName = new StringBuilder();

            // Whether we are traversing a variable name or a regular part of the string
            bool inAVariableName = false;
            // Whether there has been at least one successful expansion of the provided string
            bool atLeastOneExpansion = false;

            foreach (char c in stringToExpand)
            {
                // If the character is a variable separator and we are not parsing a variable name, then
                // this is the start of a variable name. Both Windows and non-Windows case start with a distinguished separator
                if (c == s_variableSeparator && !inAVariableName)
                {
                    inAVariableName = true;
                    // The separator is always discarded and not part of the name
                    continue;
                }
                // We are not parsing a variable name and this is a regular character of a string. Just make it part of the result
                else if (c != s_variableSeparator && !inAVariableName)
                {
                    result.Append(c);
                }
                // We are parsing a variable name and we are past the initial separator
                else
                {
                    // This is the end of the variable name
                    if (IsEndOfVariableName(c))
                    {
                        // Get the name and query the engine for the result. If the variable is not defined, just append the name unexpanded
                        // Observe that even when the variable is not defined, by querying the engine we will record this as an anti-dependency
                        var name = variableName.ToString();
                        if (!string.IsNullOrEmpty(name) && engine.TryGetBuildParameter(name, "DScript", out string value) && !string.IsNullOrEmpty(value))
                        {
                            result.Append(value);
                            atLeastOneExpansion = true;
                        }
                        else
                        {
                            AppendVariableUnexpanded(result, name);
                        }

                        // We are done parsing the variable name. Clear up the builder so subsequent names can be constructed.
                        variableName.Clear();
                        inAVariableName = false;

                        // If the end of the variable name is flagged by an explicit separator (e.g. %PATH%), then there is nothing else to do
                        // and this ending separator won't be part of the result.
                        // Otherwise the ending character can be either the start of a new variable or a regular part of the string
                        if (!EndOfVariableNameIsSeparator())
                        {
                            if (c == s_variableSeparator)
                            {
                                inAVariableName = true;
                                // The separator is always discarded and not part of the name
                                continue;
                            }
                            else
                            {
                                result.Append(c);
                            }
                        }
                    }
                    // We are parsing a variable name and the current char is part of the name
                    else
                    {
                        variableName.Append(c);
                    }
                }
            }

            // We finished traversing the string while interpreting a variable name
            if (inAVariableName)
            {
                // If a separator is needed to end a variable name, then this is a malformed name. E.g."C:\foo\%PATH"
                // So we leave it unexpanded
                if (EndOfVariableNameIsSeparator())
                {
                    result.Append(s_variableSeparator);
                    result.Append(variableName);
                }
                else
                {
                    // Otherwise, the end of the string defines the end of the name. Expand it as needed
                    var name = variableName.ToString();
                    if (!string.IsNullOrEmpty(name) && engine.TryGetBuildParameter(name, "DScript", out string value) && !string.IsNullOrEmpty(value))
                    {
                        result.Append(value);
                        atLeastOneExpansion = true;
                    }
                    else
                    {
                        AppendVariableUnexpanded(result, name);
                    }
                }
            }

            expandedString = result.ToString();
            return(atLeastOneExpansion);
        }