Skip to content

Commit

Permalink
[CLI] Support compiling for multiple targets together (#660)
Browse files Browse the repository at this point in the history
* Support multiple targets in .pproj

Adds support for multiple targets in .pproj file using <Target> field

Adds <pobserve-package> field in .pproj

* Correct output paths with multiple targets

* Correct C# test runner path

* [CLI] Update backend targets from dict to list
  • Loading branch information
aman-goel authored Oct 3, 2023
1 parent 39fb385 commit 05c9862
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 96 deletions.
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.Distinct())
{
job.OutputDirectory = Directory.CreateDirectory(Path.Combine(parentDirectory.FullName, entry.ToString()));
job.Output = new DefaultCompilerOutput(job.OutputDirectory);
job.Backend = TargetLanguage.GetCodeGenerator(entry);

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

// 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} 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 List<CompilerOutput>{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, IList<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 IList<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; }
IList<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 List<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.Add(CompilerOutput.CSharp);
break;
case "verification":
case "coverage":
case "symbolic":
case "psym":
case "pcover":
compilerConfiguration.OutputLanguages.Add(CompilerOutput.Symbolic);
break;
case "pobserve":
case "java":
compilerConfiguration.OutputLanguages.Add(CompilerOutput.Java);
break;
case "stately":
compilerConfiguration.OutputLanguages.Add(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 IList<CompilerOutput> GetTargetLanguages(FileInfo fullPathName)
{
var outputLanguages = new List<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.Add(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.Add(CompilerOutput.CSharp);
break;
case "verification":
case "coverage":
case "symbolic":
case "psym":
case "pcover":
outputLanguages.Add(CompilerOutput.Symbolic);
break;
case "pobserve":
case "java":
outputLanguages.Add(CompilerOutput.Java);
break;
case "stately":
outputLanguages.Add(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 List<CompilerOutput>{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 IList<CompilerOutput> compilerOutputs;
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(IList<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

0 comments on commit 05c9862

Please sign in to comment.