/// <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); }
/// <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); }
/// <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); }
/// <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); }