Skip to content

Avoid creating source link .json file when no source control mapping is available #989

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/Microsoft.Build.Tasks.Git/RepositoryTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ bool logAssemblyLoadingErrors()
return !Log.HasLoggedErrors;
}

private void ReportMissingRepositoryWarning(string initialPath)
{
if (!NoWarnOnMissingRepository)
{
Log.LogWarning(Resources.UnableToLocateRepository, initialPath);
}
}

private protected abstract void Execute(GitRepository repository);

protected abstract string? GetRepositoryId();
Expand Down Expand Up @@ -103,11 +111,7 @@ private void ExecuteImpl()

if (!GitRepository.TryFindRepository(initialPath, out var location))
{
if (!NoWarnOnMissingRepository)
{
Log.LogWarning(Resources.UnableToLocateRepository, initialPath);
}

ReportMissingRepositoryWarning(initialPath);
return null;
}

Expand All @@ -129,11 +133,7 @@ private void ExecuteImpl()

if (repository?.WorkingDirectory == null)
{
if (!NoWarnOnMissingRepository)
{
Log.LogWarning(Resources.UnableToLocateRepository, initialPath);
}

ReportMissingRepositoryWarning(initialPath);
repository = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
RepositoryId="$(_GitRepositoryId)"
ConfigurationScope="$(GitRepositoryConfigurationScope)"
ProjectDirectory="$(MSBuildProjectDirectory)"
Files="@(Compile)">
Files="@(Compile)"
Condition="'$(_GitRepositoryId)' != ''">

<Output TaskParameter="UntrackedFiles" ItemName="EmbeddedFiles" />
</Microsoft.Build.Tasks.Git.GetUntrackedFiles>
Expand Down
52 changes: 46 additions & 6 deletions src/SourceLink.Common.UnitTests/GenerateSourceLinkFileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the License.txt file in the project root for more information.
using System;
using System.IO;
using System.Text;
using Xunit;
using TestUtilities;
using static TestUtilities.KeyValuePairUtils;
Expand All @@ -14,23 +15,56 @@ public class GenerateSourceLinkFileTests
private static string AdjustSeparatorsInJson(string json)
=> Path.DirectorySeparatorChar == '/' ? json.Replace(@"\\", "/") : json;

[Fact]
public void Empty()
[Theory]
[CombinatorialData]
public void Empty(bool noWarning)
{
var sourceLinkFilePath = Path.Combine(TempRoot.Root, Guid.NewGuid().ToString());

var engine = new MockEngine();

var task = new GenerateSourceLinkFile()
{
BuildEngine = engine,
OutputFile = sourceLinkFilePath,
SourceRoots = new MockItem[0],
NoWarnOnMissingSourceControlInformation = noWarning,
};

var content = task.GenerateSourceLinkContent();
Assert.True(task.Execute());

AssertEx.AssertEqualToleratingWhitespaceDifferences(
"WARNING : " + string.Format(Resources.SourceControlInformationIsNotAvailableGeneratedSourceLinkEmpty), engine.Log);
noWarning ? "" : "WARNING : " + string.Format(Resources.SourceControlInformationIsNotAvailableGeneratedSourceLinkEmpty),
engine.Log);

AssertEx.AreEqual(@"{""documents"":{}}", content);
Assert.Null(task.SourceLink);
Assert.Null(task.FileWrite);
}

[Fact]
public void Empty_DeleteExistingFile()
{
using var tempRoot = new TempRoot();

var sourceLinkFile = tempRoot.CreateFile();
sourceLinkFile.WriteAllText("XYZ");

var engine = new MockEngine();

var task = new GenerateSourceLinkFile()
{
BuildEngine = engine,
OutputFile = sourceLinkFile.Path,
SourceRoots = new MockItem[0],
NoWarnOnMissingSourceControlInformation = true,
};

Assert.True(task.Execute());

AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log);

Assert.Null(task.SourceLink);
Assert.Equal(sourceLinkFile.Path, task.FileWrite);
}

[Fact]
Expand Down Expand Up @@ -145,7 +179,9 @@ public void DoesNotRewriteContentIfFileContentIsSame()
{
using var temp = new TempRoot();
var tempFile = temp.CreateFile();


tempFile.WriteAllText("XYZ");

var engine = new MockEngine();
var task = new GenerateSourceLinkFile()
{
Expand All @@ -161,6 +197,10 @@ public void DoesNotRewriteContentIfFileContentIsSame()

var beforeWriteTime = File.GetLastWriteTime(tempFile.Path);

Assert.Equal(@"{""documents"":{""/_\""_/*"":""https://raw.githubusercontent.com/repo/*""}}", File.ReadAllText(tempFile.Path, Encoding.UTF8));
Assert.Equal(tempFile.Path, task.SourceLink);
Assert.Equal(tempFile.Path, task.FileWrite);

result = task.Execute();

var afterWriteTime = File.GetLastWriteTime(tempFile.Path);
Expand Down
59 changes: 40 additions & 19 deletions src/SourceLink.Common/GenerateSourceLinkFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,23 @@ public sealed class GenerateSourceLinkFile : Task
[Required, NotNull]
public string? OutputFile { get; set; }

/// <summary>
/// Set to <see cref="OutputFile"/> if the output file was written to, null otherwise.
/// </summary>
[Output]
public string? FileWrite { get; set; }

/// <summary>
/// Set to <see cref="OutputFile"/> if the output Source Link file should be passed to the compiler.
/// </summary>
[Output]
public string? SourceLink { get; set; }

public bool NoWarnOnMissingSourceControlInformation { get; set; }

public override bool Execute()
{
var content = GenerateSourceLinkContent();
if (content != null)
{
WriteSourceLinkFile(content);
}
WriteSourceLinkFile(GenerateSourceLinkContent());

return !Log.HasLoggedErrors;
}
Expand All @@ -43,7 +51,7 @@ static string jsonEscape(string str)
result.Append("{\"documents\":{");

bool success = true;
bool first = true;
bool isEmpty = true;
foreach (var root in SourceRoots)
{
string mappedPath = root.GetMetadata(Names.SourceRoot.MappedPath);
Expand Down Expand Up @@ -79,9 +87,9 @@ static string jsonEscape(string str)
continue;
}

if (first)
if (isEmpty)
{
first = false;
isEmpty = false;
}
else
{
Expand All @@ -100,38 +108,51 @@ static string jsonEscape(string str)

result.Append("}}");

if (!success)
{
return null;
}
return success && !isEmpty ? result.ToString() : null;
}

if (first && !NoWarnOnMissingSourceControlInformation)
private void WriteSourceLinkFile(string? content)
{
if (content == null && !NoWarnOnMissingSourceControlInformation)
{
Log.LogWarning(Resources.SourceControlInformationIsNotAvailableGeneratedSourceLinkEmpty);
}

return result.ToString();
}

private void WriteSourceLinkFile(string content)
{
try
{
if (File.Exists(OutputFile))
{
if (content == null)
{
File.Delete(OutputFile);
FileWrite = OutputFile;
return;
}

var originalContent = File.ReadAllText(OutputFile);
if (originalContent == content)
{
// Don't rewrite the file if the contents are the same
// Don't rewrite the file if the contents is the same, just pass it to the compiler.
SourceLink = OutputFile;
return;
}
}
else if (content == null)
{
// File doesn't exist and the output is empty:
// Do not write the file and don't pass it to the compiler.
return;
}

File.WriteAllText(OutputFile, content);
FileWrite = SourceLink = OutputFile;
}
catch (Exception e)
{
Log.LogError(Resources.ErrorWritingToSourceLinkFile, OutputFile, e.Message);

// Part of the file might have been written.
FileWrite = OutputFile;
}
}
}
Expand Down
17 changes: 10 additions & 7 deletions src/SourceLink.Common/build/Microsoft.SourceLink.Common.targets
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<Target Name="_SetSourceLinkFilePath">
<PropertyGroup>
<SourceLink>$(IntermediateOutputPath)$(MSBuildProjectName).sourcelink.json</SourceLink>
<_SourceLinkFilePath>$(IntermediateOutputPath)$(MSBuildProjectName).sourcelink.json</_SourceLinkFilePath>
</PropertyGroup>
</Target>

Expand Down Expand Up @@ -48,20 +48,23 @@
<Target Name="_GenerateSourceLinkFile"
DependsOnTargets="_SetSourceLinkFilePath;$(_GenerateSourceLinkFileDependsOnTargets);_InitializeSourceRootMappedPathsOpt;$(SourceLinkUrlInitializerTargets)"
Condition="'$(EnableSourceLink)' == 'true' and '$(DebugType)' != 'none'"
Outputs="$(SourceLink)">
Outputs="$(_SourceLinkFilePath)">

<!--- Suppress warning if targets are imported from an SDK without a package reference. -->
<Microsoft.SourceLink.Common.GenerateSourceLinkFile
SourceRoots="@(SourceRoot)"
NoWarnOnMissingSourceControlInformation="$(PkgMicrosoft_SourceLink_Common.Equals(''))"
OutputFile="$(SourceLink)" />
OutputFile="$(_SourceLinkFilePath)">

<ItemGroup>
<FileWrites Include="$(SourceLink)" />
</ItemGroup>
<!-- Set SourceLink property passed to compilers -->
<Output TaskParameter="SourceLink" PropertyName="SourceLink" />

<!-- Log file write if the content of the file has been updated -->
<Output TaskParameter="FileWrite" ItemName="FileWrites" />
</Microsoft.SourceLink.Common.GenerateSourceLinkFile>

<!-- C++ Link task currently doesn't recognize SourceLink property -->
<ItemGroup Condition="'$(Language)' == 'C++'">
<ItemGroup Condition="'$(Language)' == 'C++' and '$(SourceLink)' != ''">
<Link Update="@(Link)">
<AdditionalOptions>%(Link.AdditionalOptions) /sourcelink:"$(SourceLink)"</AdditionalOptions>
</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public CloudHostedProvidersTests()
[ConditionalFact(typeof(DotNetSdkAvailable))]
public void NoRepository_Warnings()
{
var sourceLinkFilePath = Path.Combine(ProjectObjDir.Path, Configuration, TargetFramework, "test.sourcelink.json");

VerifyValues(
customProps: "",
customTargets: "",
Expand All @@ -36,21 +38,27 @@ public void NoRepository_Warnings()
expressions: new[]
{
"@(SourceRoot)",
"$(SourceLink)",
},
expectedResults: new[]
{
NuGetPackageFolders
NuGetPackageFolders,
"",
},
expectedWarnings: new[]
{
string.Format(Build.Tasks.Git.Resources.UnableToLocateRepository, ProjectDir.Path),
string.Format(Common.Resources.SourceControlInformationIsNotAvailableGeneratedSourceLinkEmpty),
});

Assert.False(File.Exists(sourceLinkFilePath));
}

[ConditionalFact(typeof(DotNetSdkAvailable))]
public void NoRepository_NoWarnings()
{
var sourceLinkFilePath = Path.Combine(ProjectObjDir.Path, Configuration, TargetFramework, "test.sourcelink.json");

VerifyValues(
customProps: """
<PropertyGroup>
Expand All @@ -66,11 +74,15 @@ public void NoRepository_NoWarnings()
expressions: new[]
{
"@(SourceRoot)",
"$(SourceLink)",
},
expectedResults: new[]
{
NuGetPackageFolders
NuGetPackageFolders,
"",
});

Assert.False(File.Exists(sourceLinkFilePath));
}

[ConditionalFact(typeof(DotNetSdkAvailable))]
Expand Down