Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jskeet committed Dec 13, 2024
1 parent e657473 commit aa7d9c7
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 73 deletions.
5 changes: 4 additions & 1 deletion Dockerfile.generator
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ COPY Directory.Packages.props .
COPY global.json .
COPY tools tools
COPY toolversions.sh .
COPY generator-input/CommonResourcesConfig.json generator-input/

# Build our tooling (which provides the entry point)
RUN dotnet build tools/Google.Cloud.Tools.ReleaseManager -c Release
Expand All @@ -47,4 +46,8 @@ ENV PROTOC=/app/protobuf/tools/linux_x64/protoc
ENV GRPC_PLUGIN=/app/grpc/tools/linux_x64/grpc_csharp_plugin
ENV GAPIC_PLUGIN=/app/gapic/Google.Api.Generator

# Create a really minimal generator-input in the container, for
# "unconfigured" generation.
COPY generator-input/CommonResourcesConfig.json generator-input/

ENTRYPOINT ["dotnet", "ReleaseManager/Google.Cloud.Tools.ReleaseManager.dll", "container"]
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,7 @@ private int BuildConfigured(Dictionary<string, string> options, string repoRoot)
WorkingDirectory = repoRoot
};
processArguments.ForEach(psi.ArgumentList.Add);

var process = Process.Start(psi)!;
process.WaitForExit();
if (process.ExitCode != 0)
{
Console.WriteLine("Error during build");
}
return process.ExitCode;
return ExecuteBuildProcess(psi);
}

/// <summary>
Expand All @@ -115,28 +108,33 @@ private int BuildUnconfigured(Dictionary<string, string> options, string generat
return 1;
}

// TODO: Generate a solution file, so we don't need to do this.
foreach (var directory in Directory.GetDirectories(apiRoot))
var solutions = Directory.GetFiles(apiRoot, "*.sln");
if (solutions.Length != 1)
{
var relativeDirectory = Path.GetFileName(directory);
Console.WriteLine($"Building {relativeDirectory}");
var psi = new ProcessStartInfo
{
FileName = "/usr/bin/dotnet",
WorkingDirectory = apiRoot
};
var processArguments = new List<string> { "build", "-nologo", "-clp:NoSummary", "-v", "quiet", relativeDirectory };
processArguments.ForEach(psi.ArgumentList.Add);
Console.WriteLine($"{solutions.Length} solution files in output directory. Aborting.");
return 1;
}
var solution = Path.GetFileName(solutions[0]); ;
Console.WriteLine($"Building {solution}");

var process = Process.Start(psi)!;
process.WaitForExit();
if (process.ExitCode != 0)
{
Console.WriteLine("Error during build");
return 1;
}
var psi = new ProcessStartInfo
{
FileName = "/usr/bin/dotnet",
WorkingDirectory = apiRoot
};
var processArguments = new List<string> { "build", "-nologo", "-clp:NoSummary", "-v", "quiet", solution };
processArguments.ForEach(psi.ArgumentList.Add);
return ExecuteBuildProcess(psi);
}

private static int ExecuteBuildProcess(ProcessStartInfo psi)
{
var process = Process.Start(psi)!;
process.WaitForExit();
if (process.ExitCode != 0)
{
Console.WriteLine("Error during build");
}
Console.WriteLine("Build successful");
return 0;
return process.ExitCode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public int Execute(Dictionary<string, string> options)
var generatorCommand = new GenerateApisCommand();
if (generatorInput is null)
{
// For unconfigured generation, we use a really minimal generator-input directory in the container itself.
// At the time of writing, this is only used for the common resource config (which hadn't changed between March 2021
// and December 2024, so is clearly pretty stable).
Environment.SetEnvironmentVariable(GenerateApisCommand.GeneratorInputDirectoryEnvironmentVariable, "/app/generator-input");
return generatorCommand.Execute(new[] { "--unconfigured", apiPath });
}
else
Expand Down
32 changes: 13 additions & 19 deletions tools/Google.Cloud.Tools.ReleaseManager/GenerateApisCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,14 @@ public GenerateApisCommand()

public int Execute(string[] args)
{
bool generateUnconfigured = args.FirstOrDefault() == "--unconfigured";

ValidateEnvironment(!generateUnconfigured);
ValidateEnvironment();
if (Directory.Exists(tempOutputDirectory))
{
Directory.Delete(tempOutputDirectory, true);
}
Directory.CreateDirectory(tempOutputDirectory);

if (generateUnconfigured)
if (args.FirstOrDefault() == "--unconfigured")
{
return ExecuteForUnconfigured(args.Skip(1).ToArray());
}
Expand Down Expand Up @@ -312,18 +310,15 @@ private void DeleteGeneratedFiles(string directory)
}
}

private void ValidateEnvironment(bool requireGeneratorInput)
private void ValidateEnvironment()
{
ValidateFile(protocBinary, "protoc");
ValidateFile(gapicGeneratorBinary, "GAPIC generator");
ValidateFile(grpcGeneratorBinary, "gRPC generator");
ValidateDirectory(googleApisDirectory, "googleapis");
ValidateDirectory(protobufToolsRootDirectory, "protobuf tools root");
ValidateDirectory(generatorOutputDirectory, "generator output");
if (requireGeneratorInput)
{
ValidateDirectory(generatorInputDirectory, "generator input");
}
ValidateDirectory(generatorInputDirectory, "generator input");
// This will throw if we can't detect bash.
GetBashExecutable();

Expand Down Expand Up @@ -373,14 +368,6 @@ private int ExecuteForUnconfigured(string[] args)
}
Directory.CreateDirectory(tempOutputDirectory);

// Extract CommonResourcesConfig.json from this assembly, and dump it in the temp output directory.
// This isn't great, but it's the only way of generating unconfigured without causing other problems.
using (var commonResourcesInput = typeof(GenerateApisCommand).Assembly.GetManifestResourceStream("Google.Cloud.Tools.ReleaseManager.CommonResourcesConfig.json"))
{
using var commonResourcesOutput = File.Create(Path.Combine(tempOutputDirectory, "CommonResourcesConfig.json"));
commonResourcesInput.CopyTo(commonResourcesOutput);
}

foreach (var arg in args)
{
GenerateUnconfigured(arg);
Expand Down Expand Up @@ -499,7 +486,14 @@ private void GenerateUnconfigured(string arg)
return;
}

// TODO: Generate a solution file.
var sln = Path.Combine(productionDirectory, $"{id}.sln");
var projects = new List<(string, string)>
{
(id, $@"{id}\\{id}.csproj"),
(id, $@"{id}\\{id}.Snippets.csproj"),
(id, $@"{id}\\{id}.GeneratedSnippets.csproj"),
};
NonSourceGenerator.GenerateSolutionFile(sln, projects);

IEnumerable<(string name, string value)> GetGapicPluginOptions()
{
Expand All @@ -508,7 +502,7 @@ private void GenerateUnconfigured(string arg)
yield return ("transport", "grpc");
yield return ("rest-numeric-enums", "True");
yield return ("service-config", configFiles[0]);
yield return ("common-resources-config", Path.Combine(tempOutputDirectory, "CommonResourcesConfig.json"));
yield return ("common-resources-config", Path.Combine(generatorInputDirectory, "CommonResourcesConfig.json"));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
<ProjectReference Include="..\Google.Cloud.Tools.Common\Google.Cloud.Tools.Common.csproj" />
<ProjectReference Include="..\Google.Cloud.Tools.VersionCompat\Google.Cloud.Tools.VersionCompat.csproj" />
<EmbeddedResource Include="History/*.json" />
<EmbeddedResource Include="..\..\generator-input\CommonResourcesConfig.json" />

<!--
- Refer to Grpc.Net.Client explicitly, so that smoke tests load the same TFM as
Expand Down
50 changes: 26 additions & 24 deletions tools/Google.Cloud.Tools.ReleaseManager/NonSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -447,10 +447,26 @@ private void GenerateSolutionFile(ApiMetadata api)
{
string apiRoot = GetApiDirectory(api);

// TODO: Add projects referred to via project references as well.
List<string> projects = api.DeriveProjects().ToList();
string solutionFile = Path.Combine(apiRoot, $"{api.Id}.sln");
List<(string, string)> projects = new();
foreach (var project in api.DeriveProjects())
{
projects.Add((project, $@"{project}\{project}.csproj"));
}
if (api.DeriveProjects().Any(p => p.EndsWith("Tests", StringComparison.Ordinal) || p.EndsWith("Snippets", StringComparison.Ordinal)))
{
projects.Add((ClientTestingName, RootRelativeClientTestingPath));
}
foreach (var pair in api.Dependencies.Where(pair => pair.Value == ProjectVersionValue))
{
string dependencyId = pair.Key;
projects.Add((dependencyId, $@"..\{dependencyId}\{dependencyId}\{dependencyId}.csproj"));
}
GenerateSolutionFile(solutionFile, projects);
}

internal static void GenerateSolutionFile(string file, List<(string name, string path)> projects)
{
string[] prefixLines =
{
"\uFEFF", // Byte order mark (on a line on its own), not written by File.WriteAllLines
Expand All @@ -476,27 +492,7 @@ private void GenerateSolutionFile(ApiMetadata api)
"EndGlobal"
};

foreach (var project in api.DeriveProjects())
{
AddProject(project, $@"{project}\{project}.csproj");
}
if (api.DeriveProjects().Any(p => p.EndsWith("Tests", StringComparison.Ordinal) || p.EndsWith("Snippets", StringComparison.Ordinal)))
{
AddProject(ClientTestingName, RootRelativeClientTestingPath);
}
foreach (var pair in api.Dependencies.Where(pair => pair.Value == ProjectVersionValue))
{
string dependencyId = pair.Key;
AddProject(dependencyId, $@"..\{dependencyId}\{dependencyId}\{dependencyId}.csproj");
}

var allLines = prefixLines.Concat(projectLines).Concat(betweenLines).Concat(postSolutionLines).Concat(suffixLines);
// Note: this deliberately uses the platform-default line ending. That's how our git repository is set up (core.crlf)
// so we *expect* to have CRLF on Windows and LF on Linux. We have GitHub actions to detect any CRLF files in a PR,
// so we need to make sure that when an action on Linux rewrites solution files, it doesn't actually change anything.
File.WriteAllLines(solutionFile, allLines);

void AddProject(string name, string path)
foreach (var (name, path) in projects)
{
var guid = GenerateGuid(name).ToString().ToUpperInvariant();
projectLines.Add($"Project(\"{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}\") = \"{name}\", \"{path}\", \"{{{guid}}}\"");
Expand All @@ -506,14 +502,20 @@ void AddProject(string name, string path)
postSolutionLines.Add($" {{{guid}}}.Release|Any CPU.ActiveCfg = Release|Any CPU");
postSolutionLines.Add($" {{{guid}}}.Release|Any CPU.Build.0 = Release|Any CPU");

Guid GenerateGuid(string name)
static Guid GenerateGuid(string name)
{
// Note: We're using MD5 to get 16 bytes for the GUID.
// This is only meant to be unique - it doesn't need to be cryptographically secure at all.
var bytes = MD5.HashData(Encoding.UTF8.GetBytes(name));
return new Guid(bytes);
}
}

var allLines = prefixLines.Concat(projectLines).Concat(betweenLines).Concat(postSolutionLines).Concat(suffixLines);
// Note: this deliberately uses the platform-default line ending. That's how our git repository is set up (core.crlf)
// so we *expect* to have CRLF on Windows and LF on Linux. We have GitHub actions to detect any CRLF files in a PR,
// so we need to make sure that when an action on Linux rewrites solution files, it doesn't actually change anything.
File.WriteAllLines(file, allLines);
}

private void GenerateOwlBotConfiguration(ApiMetadata api)
Expand Down

0 comments on commit aa7d9c7

Please sign in to comment.