Merge pull request #1877 from dotnet/pawelka/outname_new

Enable specifying output assembly name in compiler options
This commit is contained in:
Pawel Kadluczka 2016-03-21 14:29:23 -07:00
commit ca74a9b177
20 changed files with 196 additions and 57 deletions

View file

@ -0,0 +1,10 @@
namespace AppWithOutputAssemblyName
{
public class MyApp
{
public static void Main()
{
System.Console.WriteLine("Hello, World!");
}
}
}

View file

@ -0,0 +1,15 @@
{
"compilationOptions": {
"outputName": "MyApp",
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"dependencies": {
"NETStandard.Library": "1.0.0-rc2-23901"
},
"frameworks": {
"netstandardapp1.5": {
"imports": "dnxcore50"
}
}
}

View file

@ -0,0 +1,6 @@
namespace LibraryWithOutputAssemblyName
{
public class MyClass
{
}
}

View file

@ -0,0 +1,13 @@
{
"compilationOptions": {
"outputName": "MyLibrary"
},
"dependencies": {
"NETStandard.Library": "1.0.0-rc2-23901"
},
"frameworks": {
"netstandardapp1.5": {
"imports": "dnxcore50"
}
}
}

View file

@ -1,7 +1,8 @@
{
"version": "1.0.0-*",
"compilationOptions": {
"emitEntryPoint": true
"emitEntryPoint": true,
"outputName": "AppWithContentPackage"
},
"dependencies": {
"NETStandard.Library": "1.5.0-rc2-23911",

View file

@ -40,6 +40,8 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
internal static readonly OptionTemplate s_additionalArgumentsTemplate = new OptionTemplate("additional-argument");
internal static readonly OptionTemplate s_outputNameTemplate = new OptionTemplate("output-name");
public static CommonCompilerOptions Parse(ArgumentSyntax syntax)
{
IReadOnlyList<string> defines = null;
@ -55,6 +57,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
bool? publicSign = null;
bool? emitEntryPoint = null;
bool? generateXmlDocumentation = null;
string outputName = null;
IReadOnlyList<string> additionalArguments = null;
Func<string, bool?> nullableBoolConverter = v => bool.Parse(v);
@ -97,6 +100,8 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
syntax.DefineOption(s_generateXmlDocumentation.LongName, ref generateXmlDocumentation,
nullableBoolConverter, "Generate XML documentation file");
syntax.DefineOption(s_outputNameTemplate.LongName, ref outputName, "Output assembly name");
return new CommonCompilerOptions
{
Defines = defines,
@ -112,6 +117,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
DebugType = debugType,
EmitEntryPoint = emitEntryPoint,
GenerateXmlDocumentation = generateXmlDocumentation,
OutputName = outputName,
AdditionalArguments = additionalArguments
};
}
@ -131,6 +137,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
var publicSign = options.PublicSign;
var emitEntryPoint = options.EmitEntryPoint;
var generateXmlDocumentation = options.GenerateXmlDocumentation;
var outputName = options.OutputName;
var additionalArguments = options.AdditionalArguments;
var args = new List<string>();
@ -205,6 +212,11 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
args.Add(s_generateXmlDocumentation.ToLongArg(generateXmlDocumentation));
}
if (outputName != null)
{
args.Add(s_outputNameTemplate.ToLongArg(outputName));
}
return args;
}
}

View file

@ -28,14 +28,14 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
private readonly LibraryExporter _exporter;
private readonly string _configuration;
private readonly OutputPaths _outputPaths;
private readonly string _runtimeOutputPath;
private readonly string _intermediateOutputPath;
private readonly CommonCompilerOptions _compilerOptions;
public Executable(ProjectContext context, OutputPaths outputPaths, LibraryExporter exporter, string configuration)
{
_context = context;
@ -43,7 +43,7 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
_runtimeOutputPath = outputPaths.RuntimeOutputPath;
_intermediateOutputPath = outputPaths.IntermediateOutputDirectoryPath;
_exporter = exporter;
_configuration = configuration;
_compilerOptions = _context.ProjectFile.GetCompilerOptions(_context.TargetFramework, configuration);
}
public void MakeCompilationOutputRunnable()
@ -76,11 +76,11 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
{
WriteDepsFileAndCopyProjectDependencies(_exporter);
var emitEntryPoint = _context.ProjectFile.GetCompilerOptions(_context.TargetFramework, _configuration).EmitEntryPoint ?? false;
var emitEntryPoint = _compilerOptions.EmitEntryPoint ?? false;
if (emitEntryPoint && !string.IsNullOrEmpty(_context.RuntimeIdentifier))
{
// TODO: Pick a host based on the RID
CoreHost.CopyTo(_runtimeOutputPath, _context.ProjectFile.Name + Constants.ExeSuffix);
CoreHost.CopyTo(_runtimeOutputPath, _compilerOptions.OutputName + Constants.ExeSuffix);
}
}
@ -144,7 +144,9 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
runtimeOptions.Add("framework", framework);
}
var runtimeConfigJsonFile = Path.Combine(_runtimeOutputPath, _context.ProjectFile.Name + FileNameSuffixes.RuntimeConfigJson);
var runtimeConfigJsonFile =
Path.Combine(_runtimeOutputPath, _compilerOptions.OutputName + FileNameSuffixes.RuntimeConfigJson);
using (var writer = new JsonTextWriter(new StreamWriter(File.Create(runtimeConfigJsonFile))))
{
writer.Formatting = Formatting.Indented;
@ -155,18 +157,18 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
public void WriteDeps(LibraryExporter exporter)
{
var path = Path.Combine(_runtimeOutputPath, _context.ProjectFile.Name + FileNameSuffixes.Deps);
CreateDirectoryIfNotExists(path);
File.WriteAllLines(path, exporter
Directory.CreateDirectory(_runtimeOutputPath);
var depsFilePath = Path.Combine(_runtimeOutputPath, _compilerOptions.OutputName + FileNameSuffixes.Deps);
File.WriteAllLines(depsFilePath, exporter
.GetDependencies(LibraryType.Package)
.SelectMany(GenerateLines));
var compilerOptions = _context.ResolveCompilationOptions(_configuration);
var includeCompile = compilerOptions.PreserveCompilationContext == true;
var includeCompile = _compilerOptions.PreserveCompilationContext == true;
var exports = exporter.GetAllExports().ToArray();
var dependencyContext = new DependencyContextBuilder().Build(
compilerOptions: includeCompile ? compilerOptions : null,
compilerOptions: includeCompile ? _compilerOptions : null,
compilationExports: includeCompile ? exports : null,
runtimeExports: exports,
portable: string.IsNullOrEmpty(_context.RuntimeIdentifier),
@ -174,8 +176,8 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
runtime: _context.RuntimeIdentifier ?? string.Empty);
var writer = new DependencyContextWriter();
var depsJsonFile = Path.Combine(_runtimeOutputPath, _context.ProjectFile.Name + FileNameSuffixes.DepsJson);
using (var fileStream = File.Create(depsJsonFile))
var depsJsonFilePath = Path.Combine(_runtimeOutputPath, _compilerOptions.OutputName + FileNameSuffixes.DepsJson);
using (var fileStream = File.Create(depsJsonFilePath))
{
writer.Write(dependencyContext, fileStream);
}
@ -210,13 +212,6 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
}
}
private static void CreateDirectoryIfNotExists(string path)
{
var depsFile = new FileInfo(path);
depsFile.Directory.Create();
}
private static IEnumerable<string> GenerateLines(LibraryExport export)
{
return GenerateLines(export, export.RuntimeAssemblies, "runtime")

View file

@ -39,6 +39,8 @@ namespace Microsoft.DotNet.ProjectModel
public IEnumerable<string> AdditionalArguments { get; set; }
public string OutputName { get;set; }
public override bool Equals(object obj)
{
var other = obj as CommonCompilerOptions;
@ -57,7 +59,8 @@ namespace Microsoft.DotNet.ProjectModel
PreserveCompilationContext == other.PreserveCompilationContext &&
EnumerableEquals(Defines, other.Defines) &&
EnumerableEquals(SuppressWarnings, other.SuppressWarnings) &&
EnumerableEquals(AdditionalArguments, other.AdditionalArguments);
EnumerableEquals(AdditionalArguments, other.AdditionalArguments) &&
OutputName == other.OutputName;
}
private static bool EnumerableEquals(IEnumerable<string> left, IEnumerable<string> right)
@ -153,6 +156,11 @@ namespace Microsoft.DotNet.ProjectModel
{
result.GenerateXmlDocumentation = option.GenerateXmlDocumentation;
}
if (option.OutputName != null)
{
result.OutputName = option.OutputName;
}
}
return result;

View file

@ -40,9 +40,9 @@ namespace Microsoft.DotNet.ProjectModel
{
get
{
return Path.Combine(
BasePath,
Project.Name + OutputExtension);
var compilationOptions = Project.GetCompilerOptions(Framework, Configuration);
return Path.Combine(BasePath, compilationOptions.OutputName + OutputExtension);
}
}

View file

@ -108,7 +108,14 @@ namespace Microsoft.DotNet.ProjectModel
var targetFrameworkOptions = targetFramework != null ? GetCompilerOptions(targetFramework) : null;
// Combine all of the options
return CommonCompilerOptions.Combine(rootOptions, configurationOptions, targetFrameworkOptions);
var compilerOptions = CommonCompilerOptions.Combine(rootOptions, configurationOptions, targetFrameworkOptions);
if (compilerOptions.OutputName == null)
{
compilerOptions.OutputName = Name;
}
return compilerOptions;
}
public TargetFrameworkInformation GetTargetFramework(NuGetFramework targetFramework)

View file

@ -575,7 +575,8 @@ namespace Microsoft.DotNet.ProjectModel
DebugType = rawOptions.ValueAsString("debugType"),
EmitEntryPoint = rawOptions.ValueAsNullableBoolean("emitEntryPoint"),
GenerateXmlDocumentation = rawOptions.ValueAsNullableBoolean("xmlDoc"),
PreserveCompilationContext = rawOptions.ValueAsNullableBoolean("preserveCompilationContext")
PreserveCompilationContext = rawOptions.ValueAsNullableBoolean("preserveCompilationContext"),
OutputName = rawOptions.ValueAsString("outputName")
};
}

View file

@ -38,7 +38,9 @@ namespace Microsoft.DotNet.ProjectModel
extension = FileNameSuffixes.DotNet.DynamicLib;
}
return Path.Combine(BasePath, Project.Name + extension);
var compilationOptions = Project.GetCompilerOptions(Framework, Configuration);
return Path.Combine(BasePath, compilationOptions.OutputName + extension);
}
}

View file

@ -438,7 +438,6 @@ namespace Microsoft.DotNet.Tools.Build
CopyCompilationOutput(outputPaths);
var options = runtimeContext.ProjectFile.GetCompilerOptions(runtimeContext.TargetFramework, _args.ConfigValue);
var executable = new Executable(runtimeContext, outputPaths, libraryExporter, _args.ConfigValue);
executable.MakeCompilationOutputRunnable();

View file

@ -96,7 +96,8 @@ namespace Microsoft.DotNet.Tools.Compiler
foreach (var resourceFile in dependency.EmbeddedResources)
{
var transformedResource = resourceFile.GetTransformedFile(intermediateOutputPath);
var resourceName = ResourceManifestName.CreateManifestName(Path.GetFileName(resourceFile.ResolvedPath), context.ProjectFile.Name);
var resourceName = ResourceManifestName.CreateManifestName(
Path.GetFileName(resourceFile.ResolvedPath), compilationOptions.OutputName);
compilerArgs.Add($"--resource:\"{transformedResource}\",{resourceName}");
}
@ -119,13 +120,13 @@ namespace Microsoft.DotNet.Tools.Compiler
context.RuntimeIdentifier ?? string.Empty);
var writer = new DependencyContextWriter();
var depsJsonFile = Path.Combine(intermediateOutputPath, context.ProjectFile.Name + "dotnet-compile.deps.json");
var depsJsonFile = Path.Combine(intermediateOutputPath, compilationOptions.OutputName + "dotnet-compile.deps.json");
using (var fileStream = File.Create(depsJsonFile))
{
writer.Write(dependencyContext, fileStream);
}
compilerArgs.Add($"--resource:\"{depsJsonFile}\",{context.ProjectFile.Name}.deps.json");
compilerArgs.Add($"--resource:\"{depsJsonFile}\",{compilationOptions.OutputName}.deps.json");
}
if (!AddNonCultureResources(context.ProjectFile, compilerArgs, intermediateOutputPath))

View file

@ -26,7 +26,7 @@ namespace Microsoft.DotNet.Tools.Compiler
public class PackageGenerator
{
protected ArtifactPathsCalculator ArtifactPathsCalculator { get; }
protected Project Project { get; }
protected string Configuration { get; }
@ -38,7 +38,7 @@ namespace Microsoft.DotNet.Tools.Compiler
ArtifactPathsCalculator = artifactPathsCalculator;
Project = project;
Configuration = configuration;
}
}
public bool BuildPackage(IEnumerable<ProjectContext> contexts, List<DiagnosticMessage> packDiagnostics)
{
@ -323,7 +323,7 @@ namespace Microsoft.DotNet.Tools.Compiler
}
return Project.Name + outputExtension;
}
}
private static string GetDefaultRootOutputPath(Project project, string outputOptionValue)
{

View file

@ -193,7 +193,7 @@ namespace Microsoft.DotNet.Tools.Publish
if (options.EmitEntryPoint.GetValueOrDefault() && !string.IsNullOrEmpty(context.RuntimeIdentifier))
{
Reporter.Verbose.WriteLine($"Copying native host to output to create fully standalone output.");
PublishHost(context, outputPath);
PublishHost(context, outputPath, options);
}
RunScripts(context, ScriptNames.PostPublish, contextVariables);
@ -223,7 +223,7 @@ namespace Microsoft.DotNet.Tools.Publish
}
}
private static int PublishHost(ProjectContext context, string outputPath)
private static int PublishHost(ProjectContext context, string outputPath, CommonCompilerOptions compilationOptions)
{
if (context.TargetFramework.IsDesktop())
{
@ -239,7 +239,9 @@ namespace Microsoft.DotNet.Tools.Publish
return 1;
}
var outputBinaryName = binaryName.Equals(Constants.HostExecutableName) ? (context.ProjectFile.Name + Constants.ExeSuffix) : binaryName;
var outputBinaryName = binaryName.Equals(Constants.HostExecutableName)
? compilationOptions.OutputName + Constants.ExeSuffix
: binaryName;
var outputBinaryPath = Path.Combine(outputPath, outputBinaryName);
File.Copy(hostBinaryPath, outputBinaryPath, overwrite: true);
@ -341,8 +343,8 @@ namespace Microsoft.DotNet.Tools.Publish
}
// No RID-specific target found, use the RID-less target and publish portable
return allContexts.FirstOrDefault(c =>
Equals(c.TargetFramework, f) &&
return allContexts.FirstOrDefault(c =>
Equals(c.TargetFramework, f) &&
string.IsNullOrEmpty(c.RuntimeIdentifier));
}

View file

@ -261,9 +261,12 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
public string GetOutputExecutableName()
{
var result = _project.Name;
result += RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : "";
return result;
return _project.Name + GetExecutableExtension();
}
public string GetExecutableExtension()
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : "";
}
private string BuildArgs()

View file

@ -81,9 +81,12 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
public string GetOutputExecutable()
{
var result = _project.Name;
result += RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : "";
return result;
return _project.Name + GetExecutableExtension();
}
public string GetExecutableExtension()
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : "";
}
private string BuildArgs()

View file

@ -74,20 +74,20 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
[Fact]
public void LibraryWithAnalyzer()
{
{
var root = Temp.CreateDirectory();
var testLibDir = root.CreateDirectory("TestLibraryWithAnalyzer");
var sourceTestLibDir = Path.Combine(_testProjectsRoot, "TestLibraryWithAnalyzer");
CopyProjectToTempDir(sourceTestLibDir, testLibDir);
// run compile
var outputDir = Path.Combine(testLibDir.Path, "bin");
var testProject = GetProjectPath(testLibDir);
var buildCmd = new BuildCommand(testProject, output: outputDir, framework: DefaultFramework);
var result = buildCmd.ExecuteWithCapturedOutput();
result.Should().Pass();
Assert.Contains("CA1018", result.StdErr);
}
@ -103,7 +103,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
var testProjectDir = Path.Combine(_testProjectsRoot, "TestAppCompilationContext", "TestApp");
var testProject = Path.Combine(testProjectDir, "project.json");
var buildCommand = new BuildCommand(testProject);
buildCommand.Execute().Should().Pass();
@ -124,7 +124,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
var result = buildCommand.ExecuteWithCapturedOutput();
result.Should().Pass();
result = Command.Create(Path.Combine(outputDir, buildCommand.GetOutputExecutableName()), new string [0])
result = Command.Create(Path.Combine(outputDir, "AppWithContentPackage" + buildCommand.GetExecutableExtension()), new string [0])
.CaptureStdErr()
.CaptureStdOut()
.Execute();
@ -137,13 +137,13 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
new DirectoryInfo(outputDir).Should()
.HaveFile("config.xml");
// verify embedded resources
result.StdOut.Should().Contain("TestAppWithContentPackage.dnf.png");
result.StdOut.Should().Contain("TestAppWithContentPackage.ui.png");
result.StdOut.Should().Contain("AppWithContentPackage.dnf.png");
result.StdOut.Should().Contain("AppWithContentPackage.ui.png");
// verify 'all' language files not included
result.StdOut.Should().NotContain("TestAppWithContentPackage.dnf_all.png");
result.StdOut.Should().NotContain("TestAppWithContentPackage.ui_all.png");
result.StdOut.Should().NotContain("AppWithContentPackage.dnf_all.png");
result.StdOut.Should().NotContain("AppWithContentPackage.ui_all.png");
// verify classes
result.StdOut.Should().Contain("TestAppWithContentPackage.Foo");
result.StdOut.Should().Contain("AppWithContentPackage.Foo");
result.StdOut.Should().Contain("MyNamespace.Util");
}
@ -167,6 +167,44 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
runCommand.Execute().Should().Pass();
}
[Fact]
public void CanSetOutputAssemblyNameForLibraries()
{
var testInstance =
TestAssetsManager
.CreateTestInstance("LibraryWithOutputAssemblyName")
.WithLockFiles();
var root = testInstance.TestRoot;
var outputDir = Path.Combine(root, "bin");
var testProject = ProjectUtils.GetProjectJson(root, "LibraryWithOutputAssemblyName");
var buildCommand = new BuildCommand(testProject, output: outputDir, framework: DefaultFramework);
var result = buildCommand.ExecuteWithCapturedOutput();
result.Should().Pass();
new DirectoryInfo(outputDir).Should().HaveFiles(new [] { "MyLibrary.dll" });
}
[Fact]
public void CanSetOutputAssemblyNameForApps()
{
var testInstance =
TestAssetsManager
.CreateTestInstance("AppWithOutputAssemblyName")
.WithLockFiles();
var root = testInstance.TestRoot;
var outputDir = Path.Combine(root, "bin");
var testProject = ProjectUtils.GetProjectJson(root, "AppWithOutputAssemblyName");
var buildCommand = new BuildCommand(testProject, output: outputDir, framework: DefaultFramework);
var result = buildCommand.ExecuteWithCapturedOutput();
result.Should().Pass();
new DirectoryInfo(outputDir).Should().HaveFiles(
new [] { "MyApp.dll", "MyApp" + buildCommand.GetExecutableExtension(),
"MyApp.runtimeconfig.json", "MyApp.deps", "MyApp.deps.json" });
}
private void CopyProjectToTempDir(string projectDir, TempDirectory tempDir)
{
// copy all the files to temp dir

View file

@ -193,5 +193,28 @@ namespace Microsoft.DotNet.Tools.Publish.Tests
result.Should().HaveStdOutMatching("\nprepublish_output( \\?[^%]+\\?){5}.+\npostpublish_output( \\?[^%]+\\?){5}", RegexOptions.Singleline);
result.Should().Pass();
}
public void PublishAppWithOutputAssemblyName()
{
TestInstance instance =
TestAssetsManager
.CreateTestInstance("AppWithOutputAssemblyName")
.WithLockFiles()
.WithBuildArtifacts();
var testRoot = _getProjectJson(instance.TestRoot, "AppWithOutputAssemblyName");
var publishCommand = new PublishCommand(testRoot, output: testRoot);
publishCommand.Execute().Should().Pass();
var publishedDir = publishCommand.GetOutputDirectory();
var extension = publishCommand.GetExecutableExtension();
var outputExe = "MyApp" + extension;
publishedDir.Should().HaveFiles(new[] { "MyApp.dll", outputExe });
publishedDir.Should().NotHaveFile("AppWithOutputAssemblyName" + extension);
publishedDir.Should().NotHaveFile("AppWithOutputAssemblyName.dll");
var command = new TestCommand(Path.Combine(publishedDir.FullName, outputExe));
command.Execute("").Should().ExitWith(0);
}
}
}