Skip to content
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

[CLI] Support compiling for multiple targets together #660

Merged
merged 4 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
60 changes: 36 additions & 24 deletions Src/PCompiler/CompilerCore/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,38 +53,50 @@ public int Compile(ICompilerConfiguration job)
IRTransformer.SimplifyMethod(fun);
}

job.Output.WriteInfo($"Code generation ...");
// Run the selected backend on the project and write the files.
var compiledFiles = job.Backend.GenerateCode(job, scope);
foreach (var file in compiledFiles)
{
job.Output.WriteInfo($"Generated {file.FileName}.");
job.Output.WriteFile(file);
}

// Not every backend has a compilation stage following code generation.
// For those that do, execute that stage.
if (job.Backend.HasCompilationStage)
DirectoryInfo parentDirectory = job.OutputDirectory;
foreach (var entry in job.OutputLanguages)
{
job.OutputDirectory = Directory.CreateDirectory(Path.Combine(parentDirectory.FullName, entry.Key));
job.Output = new DefaultCompilerOutput(job.OutputDirectory);
job.Backend = TargetLanguage.GetCodeGenerator(entry.Value);

job.Output.WriteInfo($"----------------------------------------");
job.Output.WriteInfo($"Compiling {job.ProjectName}...");
try
job.Output.WriteInfo($"Code generation for {entry.Key}...");

// Run the selected backend on the project and write the files.
var compiledFiles = job.Backend.GenerateCode(job, scope);
foreach (var file in compiledFiles)
{
job.Backend.Compile(job);
job.Output.WriteInfo($"Generated {file.FileName}.");
job.Output.WriteFile(file);
}
catch (TranslationException e)

// Not every backend has a compilation stage following code generation.
// For those that do, execute that stage.
if (job.Backend.HasCompilationStage)
{
job.Output.WriteError("[Compiling Generated Code:]\n" + e.Message);
job.Output.WriteError("[THIS SHOULD NOT HAVE HAPPENED, please report it to the P team or create a GitHub issue]\n" + e.Message);
Environment.ExitCode = 2;
return Environment.ExitCode;
job.Output.WriteInfo($"Compiling generated code...");
try
{
job.Backend.Compile(job);
}
catch (TranslationException e)
{
job.Output.WriteError($"[{entry.Key} Compiling Generated Code:]\n" + e.Message);
job.Output.WriteError("[THIS SHOULD NOT HAVE HAPPENED, please report it to the P team or create a GitHub issue]\n" + e.Message);
Environment.ExitCode = 2;
return Environment.ExitCode;
}
}
else
{
job.Output.WriteInfo($"Build succeeded.");
}
}
else
{
job.Output.WriteInfo("Build succeeded.");
}

job.Output.WriteInfo($"----------------------------------------");
job.Output.WriteInfo($"Compilation succeeded.");

Environment.ExitCode = 0;
return Environment.ExitCode;
}
Expand Down
18 changes: 9 additions & 9 deletions Src/PCompiler/CompilerCore/CompilerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ public CompilerConfiguration()
ProjectRootPath = new DirectoryInfo(Directory.GetCurrentDirectory());
LocationResolver = new DefaultLocationResolver();
Handler = new DefaultTranslationErrorHandler(LocationResolver);
OutputLanguage = CompilerOutput.CSharp;
Backend = TargetLanguage.GetCodeGenerator(OutputLanguage);
OutputLanguages = new Dictionary<string, CompilerOutput>{{"CSharp", CompilerOutput.CSharp}};
Backend = null;
ProjectDependencies = new List<string>();
}
public CompilerConfiguration(ICompilerOutput output, DirectoryInfo outputDir, CompilerOutput outputLanguage, IList<string> inputFiles,
string projectName, DirectoryInfo projectRoot = null, IList<string> projectDependencies = null)
public CompilerConfiguration(ICompilerOutput output, DirectoryInfo outputDir, IDictionary<string, CompilerOutput> outputLanguages, IList<string> inputFiles,
string projectName, DirectoryInfo projectRoot = null, IList<string> projectDependencies = null, string pObservePackageName = null)
{
if (!inputFiles.Any())
{
Expand All @@ -48,18 +48,18 @@ public CompilerConfiguration(ICompilerOutput output, DirectoryInfo outputDir, Co
}
}
ProjectName = projectName ?? Path.GetFileNameWithoutExtension(inputFiles[0]);
PObservePackageName = $"{ProjectName}.pobserve";
PObservePackageName = pObservePackageName ?? $"{ProjectName}.pobserve";
ProjectRootPath = projectRoot;
LocationResolver = new DefaultLocationResolver();
Handler = new DefaultTranslationErrorHandler(LocationResolver);
OutputLanguage = outputLanguage;
Backend = TargetLanguage.GetCodeGenerator(outputLanguage);
OutputLanguages = outputLanguages;
Backend = null;
ProjectDependencies = projectDependencies ?? new List<string>();
}

public ICompilerOutput Output { get; set; }
public DirectoryInfo OutputDirectory { get; set; }
public CompilerOutput OutputLanguage { get; set; }
public IDictionary<string, CompilerOutput> OutputLanguages { get; set; }
public string ProjectName { get; set; }
public string PObservePackageName { get; set; }
public DirectoryInfo ProjectRootPath { get; set; }
Expand All @@ -83,7 +83,7 @@ public void Copy(CompilerConfiguration parsedConfig)
ProjectDependencies = parsedConfig.ProjectDependencies;
ProjectName = parsedConfig.ProjectName;
PObservePackageName = parsedConfig.PObservePackageName;
OutputLanguage = parsedConfig.OutputLanguage;
OutputLanguages = parsedConfig.OutputLanguages;
ProjectRootPath = parsedConfig.ProjectRootPath;
}
}
Expand Down
8 changes: 4 additions & 4 deletions Src/PCompiler/CompilerCore/ICompilerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ public interface ICompilerConfiguration
string ProjectName { get; }
string PObservePackageName { get; }
DirectoryInfo ProjectRootPath { get; }
CompilerOutput OutputLanguage { get; }
ICompilerOutput Output { get; }
DirectoryInfo OutputDirectory { get; }
ICodeGenerator Backend { get; }
public IDictionary<string, CompilerOutput> OutputLanguages { get; }
ICompilerOutput Output { get; set; }
DirectoryInfo OutputDirectory { get; set; }
ICodeGenerator Backend { get; set; }
IList<string> InputPFiles { get; }
IList<string> InputForeignFiles { get; }
IList<string> ProjectDependencies { get; }
Expand Down
35 changes: 22 additions & 13 deletions Src/PCompiler/PCommandLine/Options/PCompilerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,17 +158,29 @@ private static void UpdateConfigurationWithParsedArgument(CompilerConfiguration
compilerConfiguration.ProjectName = (string)option.Value;
break;
case "mode":
compilerConfiguration.OutputLanguages = new Dictionary<string, CompilerOutput>();
switch (((string)option.Value).ToLowerInvariant())
{
compilerConfiguration.OutputLanguage = (string)option.Value switch
{
"bugfinding" => CompilerOutput.CSharp,
"verification" => CompilerOutput.Symbolic,
"coverage" => CompilerOutput.Symbolic,
"pobserve" => CompilerOutput.Java,
"stately" => CompilerOutput.Stately,
_ => compilerConfiguration.OutputLanguage
};
compilerConfiguration.Backend = TargetLanguage.GetCodeGenerator(compilerConfiguration.OutputLanguage);
case "bugfinding":
case "csharp":
compilerConfiguration.OutputLanguages["CSharp"] = CompilerOutput.CSharp;
break;
case "verification":
case "coverage":
case "symbolic":
case "psym":
case "pcover":
compilerConfiguration.OutputLanguages["Symbolic"] = CompilerOutput.Symbolic;
break;
case "pobserve":
case "java":
compilerConfiguration.OutputLanguages["Java"] = CompilerOutput.Java;
break;
case "stately":
compilerConfiguration.OutputLanguages["Stately"] = CompilerOutput.Stately;
break;
default:
throw new Exception($"Unexpected mode: '{option.Value}'");
}
break;
case "pobserve-package":
Expand Down Expand Up @@ -234,9 +246,6 @@ private static void SanitizeConfiguration(CompilerConfiguration compilerConfigur
compilerConfiguration.OutputDirectory = Directory.CreateDirectory("PGenerated");
compilerConfiguration.Output = new DefaultCompilerOutput(compilerConfiguration.OutputDirectory);
}

compilerConfiguration.OutputDirectory = Directory.CreateDirectory(Path.Combine(compilerConfiguration.OutputDirectory.FullName, compilerConfiguration.OutputLanguage.ToString()));
compilerConfiguration.Output = new DefaultCompilerOutput(compilerConfiguration.OutputDirectory);
}


Expand Down
96 changes: 63 additions & 33 deletions Src/PCompiler/PCommandLine/Parser/ParsePProjectFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using PChecker;
using PChecker.IO.Debugging;
using Plang.Compiler;
using Debug = System.Diagnostics.Debug;

namespace Plang.Parser
{
Expand All @@ -30,7 +31,6 @@ public void ParseProjectFileForCompiler(string projectFile, out CompilerConfigur
CommandLineOutput.WriteInfo($"----------------------------------------");
CommandLineOutput.WriteInfo($"==== Loading project file: {projectFile}");

var outputLanguage = CompilerOutput.CSharp;
var inputFiles = new HashSet<string>();
var projectDependencies = new HashSet<string>();

Expand All @@ -50,9 +50,17 @@ public void ParseProjectFileForCompiler(string projectFile, out CompilerConfigur

// get output directory
var outputDirectory = GetOutputDirectory(projectFilePath);

// get targets
var outputLanguages = GetTargetLanguages(projectFilePath);

// get pobserve package name
var pObservePackageName = GetPObservePackage(projectFilePath);

job = new CompilerConfiguration(output: new DefaultCompilerOutput(outputDirectory), outputDir: outputDirectory,
outputLanguage: outputLanguage, inputFiles: inputFiles.ToList(), projectName: projectName, projectRoot: projectFilePath.Directory, projectDependencies: projectDependencies.ToList());
outputLanguages: outputLanguages, inputFiles: inputFiles.ToList(), projectName: projectName,
projectRoot: projectFilePath.Directory, projectDependencies: projectDependencies.ToList(),
pObservePackageName: pObservePackageName);

CommandLineOutput.WriteInfo($"----------------------------------------");
}
Expand Down Expand Up @@ -155,6 +163,23 @@ private string GetProjectName(FileInfo projectFullPath)
return projectName;
}

/// <summary>
/// Parse the PObserve package name from the pproj file
/// </summary>
/// <param name="projectFullPath">Path to the pproj file</param>
/// <returns>pobserve package name</returns>
private string GetPObservePackage(FileInfo projectFullPath)
{
string pObservePackageName = null;
var projectXml = XElement.Load(projectFullPath.FullName);
if (projectXml.Elements("pobserve-package").Any())
{
pObservePackageName = projectXml.Element("pobserve-package")?.Value;
}

return pObservePackageName;
}

/// <summary>
/// Parse the output directory information from the pproj file
/// </summary>
Expand Down Expand Up @@ -183,43 +208,48 @@ private string GetOutputDirectoryName(FileInfo fullPathName)
return Directory.GetCurrentDirectory();
}

private void GetTargetLanguage(FileInfo fullPathName, ref CompilerOutput outputLanguage, ref bool generateSourceMaps)
private IDictionary<string, CompilerOutput> GetTargetLanguages(FileInfo fullPathName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the return value a dictionary and not a list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to list via dd21650

{
var outputLanguages = new Dictionary<string, CompilerOutput>();
var projectXml = XElement.Load(fullPathName.FullName);
if (!projectXml.Elements("Target").Any()) return;
switch (projectXml.Element("Target")?.Value.ToLowerInvariant())
if (!projectXml.Elements("Target").Any())
{
case "c":
outputLanguage = CompilerOutput.C;
// check for generate source maps attribute
try
{
if (projectXml.Element("Target")!.Attributes("sourcemaps").Any())
{
generateSourceMaps = bool.Parse(projectXml.Element("Target")?.Attribute("sourcemaps")?.Value ?? string.Empty);
}
}
catch (Exception)
outputLanguages["CSharp"] = CompilerOutput.CSharp;
}
else
{
string[] values = projectXml.Element("Target")?.Value.Split(new[] { ',', ' ' },
StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < values!.Length; i++)
{
switch (values[i].ToLowerInvariant())
{
throw new CommandlineParsingError($"Expected true or false, received {projectXml.Element("Target")?.Attribute("sourcemaps")?.Value}");
case "bugfinding":
case "csharp":
outputLanguages["CSharp"] = CompilerOutput.CSharp;
break;
case "verification":
case "coverage":
case "symbolic":
case "psym":
case "pcover":
outputLanguages["Symbolic"] = CompilerOutput.Symbolic;
break;
case "pobserve":
case "java":
outputLanguages["Java"] = CompilerOutput.Java;
break;
case "stately":
outputLanguages["Stately"] = CompilerOutput.Stately;
break;
default:
throw new CommandlineParsingError(
$"Expected CSharp, Java, Stately, or Symbolic as target, received {projectXml.Element("Target")?.Value}");
}
break;

case "csharp":
outputLanguage = CompilerOutput.CSharp;
break;

case "java":
outputLanguage = CompilerOutput.Java;
break;

case "symbolic":
outputLanguage = CompilerOutput.Symbolic;
break;

default:
throw new CommandlineParsingError($"Expected c, csharp, java, or symbolic as target, received {projectXml.Element("Target")?.Value}");
}
}

return outputLanguages;
}

/// <summary>
Expand Down
3 changes: 2 additions & 1 deletion Tst/UnitTests/Core/TestCaseFactory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Plang.Compiler;
Expand Down Expand Up @@ -86,7 +87,7 @@ public CompilerTestCase CreateTestCase(DirectoryInfo testDir)
ICompilerTestRunner runner;
ITestResultsValidator validator;

var output = CompilerOutput.C;
var output = new Dictionary<string, CompilerOutput>{{"C", CompilerOutput.C}};
runner = new CompileOnlyRunner(output, inputFiles.Select(x => x.FullName).ToList());

// TODO: validate information about the particular kind of compiler error
Expand Down
10 changes: 5 additions & 5 deletions Tst/UnitTests/Runners/CompileOnlyRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ namespace UnitTests.Runners
/// </summary>
public class CompileOnlyRunner : ICompilerTestRunner
{
private readonly CompilerOutput compilerOutput;
private readonly IDictionary<string, CompilerOutput> compilerOutputs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a dictornary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Don't need a dictionary in this final version. Update to list via dd21650

private readonly IList<string> inputFiles;

/// <summary>
/// Box a new compile runner
/// </summary>
/// <param name="compilerOutput"></param>
/// <param name="compilerOutputs"></param>
/// <param name="inputFiles">The P source files to compile</param>
public CompileOnlyRunner(CompilerOutput compilerOutput, IList<string> inputFiles)
public CompileOnlyRunner(IDictionary<string, CompilerOutput> compilerOutputs, IList<string> inputFiles)
{
this.inputFiles = inputFiles;
this.compilerOutput = compilerOutput;
this.compilerOutputs = compilerOutputs;
}

/// <inheritdoc />
Expand All @@ -45,7 +45,7 @@ public CompileOnlyRunner(CompilerOutput compilerOutput, IList<string> inputFiles
var stderrWriter = new StringWriter();
var outputStream = new TestCaseOutputStream(stdoutWriter, stderrWriter);

var job = new CompilerConfiguration(outputStream, scratchDirectory, compilerOutput, inputFiles, Path.GetFileNameWithoutExtension(inputFiles.First()));
var job = new CompilerConfiguration(outputStream, scratchDirectory, compilerOutputs, inputFiles, Path.GetFileNameWithoutExtension(inputFiles.First()));

try
{
Expand Down
Loading
Loading