예제 #1
0
        /// <summary>
        /// Attempts to create a unique string identifier for a pip based off the pip's declared outputs.
        /// All outputs in BuildXL are unique to one pip (except for shared opaque directories),
        /// so the first output declared can be used to identify a pip across builds.
        /// </summary>
        /// <returns>
        /// True, if a hash can be computed based off a pip's declared outputs that would reliably and uniquely identify the pip across pips;
        /// otherwise, false.
        /// </returns>
        public bool TryComputePipUniqueOutputHash(PathTable pathTable, out long pipUniqueOutputHash, PathExpander pathExpander = null)
        {
            // All pips must have produce at least one output
            Contract.Assert(FileOutputs.Length + DirectoryOutputs.Length > 0);

            if (m_cachedUniqueOutputHash.HasValue)
            {
                pipUniqueOutputHash = m_cachedUniqueOutputHash.Value;
                return(true);
            }

            pipUniqueOutputHash = -1;

            AbsolutePath outputPath;
            int          rewriteCount = -1;

            // Arbitrarily use file outputs before directory outputs
            if (FileOutputs.IsValid && FileOutputs.Length > 0)
            {
                var output = FileOutputs[0];
                outputPath   = output.Path;
                rewriteCount = output.RewriteCount;
            }
            else
            {
                var output = DirectoryOutputs[0];

                // There can only be one pip producer for a directory output (opaque directory) with the exception of
                // shared opaque directories.
                // There is no good way to differentiate two pips whose only declared output is the same shared opaque directory,
                // so this function should conservatively bail out for shared opaques.
                if (output.IsSharedOpaque)
                {
                    return(false);
                }

                outputPath = DirectoryOutputs[0].Path;
            }

            var pathString = pathExpander == null?outputPath.ToString(pathTable) : pathExpander.ExpandPath(pathTable, outputPath);

            // A file can have more than one pip producer, so the rewrite count must also be included in the hash
            pipUniqueOutputHash = HashCodeHelper.GetOrdinalIgnoreCaseHashCode64(pathString + UniqueOutputHashDelimiter + rewriteCount.ToString());

            m_cachedUniqueOutputHash = pipUniqueOutputHash;
            return(true);
        }
예제 #2
0
        /// <summary>
        /// Adds a <see cref="AbsolutePath" /> to the fingerprint stream without adding a type prefix.
        /// The path is added in an expanded string form.
        /// See <see cref="Add(string, AbsolutePath)" />.
        /// </summary>
        private void AddInnerPath(AbsolutePath path)
        {
            string pathString = path.IsValid ? m_pathExpander.ExpandPath(m_pathTable, path) : "??Invalid";

            AddInnerString(pathString, forceUppercase: true);
        }
예제 #3
0
        /// <nodoc />
        public void Serialize(
            PathTable pathTable,
            BuildXLWriter writer,
            PathExpander pathExpander = null,
            Action <BuildXLWriter, AbsolutePath> pathWriter = null,
            Action <BuildXLWriter, StringId> stringWriter   = null)
        {
            // We allow duplicates on construction (and deserialization), but attempt to remove them here.
            // This isn't required for correctness, but may result in storing fewer PathSets.
            int countWithoutDuplicates = Paths.Length == 0 ? 0 : 1;

            for (int i = 1; i < Paths.Length; i++)
            {
                if (Paths[i - 1].Path != Paths[i].Path)
                {
                    countWithoutDuplicates++;
                }
            }

            writer.WriteCompact(countWithoutDuplicates);

            AbsolutePath last         = AbsolutePath.Invalid;
            string       lastExpanded = null;

            foreach (ObservedPathEntry entry in Paths)
            {
                if (last == entry.Path)
                {
                    continue;
                }

                writer.Write((byte)entry.Flags);
                if (entry.DirectoryEnumerationWithCustomPattern)
                {
                    writer.Write(entry.EnumeratePatternRegex);
                }

                if (pathWriter != null)
                {
                    pathWriter(writer, entry.Path);
                }
                else
                {
                    // Try to tokenize the path if the pathExpander is given.
                    string expanded = pathExpander?.ExpandPath(pathTable, entry.Path) ?? entry.Path.ToString(pathTable);
                    if (!OperatingSystemHelper.IsUnixOS)
                    {
                        expanded = expanded.ToUpperInvariant();
                    }

                    int reuseCount = 0;
                    if (lastExpanded != null)
                    {
                        int limit = Math.Min(lastExpanded.Length, expanded.Length);
                        for (; reuseCount < limit && expanded[reuseCount] == lastExpanded[reuseCount]; reuseCount++)
                        {
                            ;
                        }
                    }

                    if (OperatingSystemHelper.IsUnixOS && reuseCount == 1)
                    {
                        // As the root is denoted via '/' but the same symbol is also used as path separator,
                        // we cannot reuse the root path only when paths differ from the 2nd char onwards.
                        reuseCount = 0;
                    }

                    writer.WriteCompact(reuseCount);
                    writer.Write(reuseCount == 0 ? expanded : expanded.Substring(reuseCount));
                    lastExpanded = expanded;
                }

                last = entry.Path;
            }

            // Serializing observedAccessedFileNames
            int fileNameCountWithoutDuplicates = ObservedAccessedFileNames.Length == 0 ? 0 : 1;

            for (int i = 1; i < ObservedAccessedFileNames.Length; i++)
            {
                if (ObservedAccessedFileNames[i - 1] != ObservedAccessedFileNames[i])
                {
                    fileNameCountWithoutDuplicates++;
                }
            }

            writer.WriteCompact(fileNameCountWithoutDuplicates);
            StringId lastFileName = StringId.Invalid;

            foreach (var entry in ObservedAccessedFileNames)
            {
                if (lastFileName == entry)
                {
                    continue;
                }

                if (stringWriter != null)
                {
                    stringWriter(writer, entry);
                }
                else
                {
                    writer.Write(entry.ToString(pathTable.StringTable));
                }
            }

            // Serializing UnsafeOptions
            UnsafeOptions.Serialize(writer);
        }
예제 #4
0
        /// <summary>
        /// Adds a <see cref="AbsolutePath" /> to the fingerprint stream without adding a type prefix.
        /// The path is added in an expanded string form.
        /// See <see cref="Add(string, AbsolutePath)" />.
        /// </summary>
        private void AddInnerPath(AbsolutePath path)
        {
            string pathString = path.IsValid ? m_pathExpander.ExpandPath(m_pathTable, path) : "??Invalid";

            AddInnerString(pathString, forceUppercase: !OperatingSystemHelper.IsPathComparisonCaseSensitive);
        }