2016-03-08 15:22:33 -08:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.IO.Compression ;
2016-03-08 19:32:31 -08:00
using System.Text.RegularExpressions ;
2016-03-10 03:19:00 -08:00
using System.Reflection.PortableExecutable ;
2016-03-08 15:22:33 -08:00
using System.Runtime.InteropServices ;
using Microsoft.DotNet.Cli.Build.Framework ;
using Microsoft.Extensions.PlatformAbstractions ;
using static Microsoft . DotNet . Cli . Build . Framework . BuildHelpers ;
2016-03-15 10:42:59 -07:00
using Newtonsoft.Json.Linq ;
using Newtonsoft.Json ;
2016-03-08 15:22:33 -08:00
namespace Microsoft.DotNet.Cli.Build
{
public class SharedFrameworkTargets
{
2016-03-14 12:17:15 -07:00
public const string SharedFrameworkName = "Microsoft.NETCore.App" ;
2016-03-08 19:32:31 -08:00
2016-03-08 15:22:33 -08:00
private const string CoreHostBaseName = "corehost" ;
2016-03-14 14:41:30 -07:00
private const string DotnetHostFxrBaseName = "hostfxr" ;
private const string HostPolicyBaseName = "hostpolicy" ;
2016-03-08 15:22:33 -08:00
2016-03-14 12:17:15 -07:00
[Target(nameof(PackageSharedFramework), nameof(CrossGenAllManagedAssemblies))]
2016-03-08 19:32:31 -08:00
public static BuildTargetResult PublishSharedFramework ( BuildTargetContext c )
2016-03-14 12:17:15 -07:00
{
return c . Success ( ) ;
}
[Target]
public static BuildTargetResult PackageSharedFramework ( BuildTargetContext c )
2016-03-08 19:32:31 -08:00
{
string SharedFrameworkPublishRoot = Path . Combine ( Dirs . Output , "obj" , "sharedframework" ) ;
string SharedFrameworkSourceRoot = Path . Combine ( Dirs . RepoRoot , "src" , "sharedframework" , "framework" ) ;
string SharedFrameworkNugetVersion = GetVersionFromProjectJson ( Path . Combine ( Path . Combine ( Dirs . RepoRoot , "src" , "sharedframework" , "framework" ) , "project.json" ) ) ;
if ( Directory . Exists ( SharedFrameworkPublishRoot ) )
{
2016-03-15 14:06:54 -07:00
Utils . DeleteDirectory ( SharedFrameworkPublishRoot ) ;
2016-03-08 19:32:31 -08:00
}
// We publish to a sub folder of the PublishRoot so tools like heat and zip can generate folder structures easier.
string SharedFrameworkNameAndVersionRoot = Path . Combine ( SharedFrameworkPublishRoot , "shared" , SharedFrameworkName , SharedFrameworkNugetVersion ) ;
2016-03-14 12:17:15 -07:00
string publishFramework = "dnxcore50" ; // Temporary, use "netcoreapp" when we update nuget.
string publishRuntime ;
if ( PlatformServices . Default . Runtime . OperatingSystemPlatform = = Platform . Windows )
{
publishRuntime = $"win7-{PlatformServices.Default.Runtime.RuntimeArchitecture}" ;
}
else
{
publishRuntime = PlatformServices . Default . Runtime . GetRuntimeIdentifier ( ) ;
}
DotNetCli . Stage2 . Publish (
"--output" , SharedFrameworkNameAndVersionRoot ,
"-r" , publishRuntime ,
"-f" , publishFramework ,
SharedFrameworkSourceRoot ) . Execute ( ) . EnsureSuccessful ( ) ;
2016-03-08 19:32:31 -08:00
c . BuildContext [ "SharedFrameworkPublishRoot" ] = SharedFrameworkPublishRoot ;
c . BuildContext [ "SharedFrameworkNugetVersion" ] = SharedFrameworkNugetVersion ;
// Clean up artifacts that dotnet-publish generates which we don't need
File . Delete ( Path . Combine ( SharedFrameworkNameAndVersionRoot , $"framework{Constants.ExeSuffix}" ) ) ;
File . Delete ( Path . Combine ( SharedFrameworkNameAndVersionRoot , "framework.dll" ) ) ;
File . Delete ( Path . Combine ( SharedFrameworkNameAndVersionRoot , "framework.pdb" ) ) ;
2016-03-15 10:42:59 -07:00
File . Delete ( Path . Combine ( SharedFrameworkNameAndVersionRoot , "framework.runtimeconfig.json" ) ) ;
2016-03-08 19:32:31 -08:00
// Rename the .deps file
2016-03-15 10:42:59 -07:00
var destinationDeps = Path . Combine ( SharedFrameworkNameAndVersionRoot , $"{SharedFrameworkName}.deps.json" ) ;
2016-03-08 19:32:31 -08:00
File . Move ( Path . Combine ( SharedFrameworkNameAndVersionRoot , "framework.deps" ) , Path . Combine ( SharedFrameworkNameAndVersionRoot , $"{SharedFrameworkName}.deps" ) ) ;
2016-03-15 10:42:59 -07:00
File . Move ( Path . Combine ( SharedFrameworkNameAndVersionRoot , "framework.deps.json" ) , destinationDeps ) ;
2016-03-15 16:27:30 -07:00
// Generate RID fallback graph
string runtimeGraphGeneratorRuntime = null ;
switch ( PlatformServices . Default . Runtime . OperatingSystemPlatform )
2016-03-15 10:42:59 -07:00
{
2016-03-15 16:27:30 -07:00
case Platform . Windows :
runtimeGraphGeneratorRuntime = "win" ;
break ;
case Platform . Linux :
runtimeGraphGeneratorRuntime = "linux" ;
break ;
case Platform . Darwin :
runtimeGraphGeneratorRuntime = "osx" ;
break ;
}
if ( ! string . IsNullOrEmpty ( runtimeGraphGeneratorRuntime ) )
{
var runtimeGraphGeneratorName = "RuntimeGraphGenerator" ;
var runtimeGraphGeneratorProject = Path . Combine ( c . BuildContext . BuildDirectory , "tools" , runtimeGraphGeneratorName ) ;
var runtimeGraphGeneratorOutput = Path . Combine ( Dirs . Output , "tools" , runtimeGraphGeneratorName ) ;
DotNetCli . Stage2 . Publish (
"--output" , runtimeGraphGeneratorOutput ,
runtimeGraphGeneratorProject ) . Execute ( ) . EnsureSuccessful ( ) ;
var runtimeGraphGeneratorExe = Path . Combine ( runtimeGraphGeneratorOutput , $"{runtimeGraphGeneratorName}{Constants.ExeSuffix}" ) ;
Cmd ( runtimeGraphGeneratorExe , "--project" , SharedFrameworkSourceRoot , "--deps" , destinationDeps , runtimeGraphGeneratorRuntime )
. Execute ( ) ;
2016-03-15 10:42:59 -07:00
}
else
{
2016-03-15 16:27:30 -07:00
c . Error ( $"Could not determine rid graph generation runtime for platform {PlatformServices.Default.Runtime.OperatingSystemPlatform}" ) ;
2016-03-15 10:42:59 -07:00
}
2016-03-08 19:32:31 -08:00
2016-03-14 14:41:30 -07:00
// corehost will be renamed to dotnet at some point and then we will not need to rename it here.
File . Copy (
Path . Combine ( Dirs . Corehost , $"{CoreHostBaseName}{Constants.ExeSuffix}" ) ,
Path . Combine ( SharedFrameworkNameAndVersionRoot , $"dotnet{Constants.ExeSuffix}" ) ) ;
File . Copy (
Path . Combine ( Dirs . Corehost , $"{Constants.DynamicLibPrefix}{HostPolicyBaseName}{Constants.DynamicLibSuffix}" ) ,
2016-03-14 15:40:19 -07:00
Path . Combine ( SharedFrameworkNameAndVersionRoot , $"{Constants.DynamicLibPrefix}{HostPolicyBaseName}{Constants.DynamicLibSuffix}" ) , true ) ;
2016-03-08 19:32:31 -08:00
2016-03-10 03:19:00 -08:00
if ( File . Exists ( Path . Combine ( SharedFrameworkNameAndVersionRoot , "mscorlib.ni.dll" ) ) )
{
// Publish already places the crossgen'd version of mscorlib into the output, so we can
// remove the IL version
File . Delete ( Path . Combine ( SharedFrameworkNameAndVersionRoot , "mscorlib.dll" ) ) ;
2016-03-14 12:17:15 -07:00
c . BuildContext [ "SharedFrameworkNameAndVersionRoot" ] = SharedFrameworkNameAndVersionRoot ;
2016-03-10 03:19:00 -08:00
}
2016-03-08 19:32:31 -08:00
return c . Success ( ) ;
}
2016-03-08 15:22:33 -08:00
[Target]
public static BuildTargetResult PublishSharedHost ( BuildTargetContext c )
{
string SharedHostPublishRoot = Path . Combine ( Dirs . Output , "obj" , "sharedhost" ) ;
2016-03-15 14:06:54 -07:00
2016-03-14 15:40:19 -07:00
if ( Directory . Exists ( SharedHostPublishRoot ) )
{
2016-03-15 14:06:54 -07:00
Utils . DeleteDirectory ( SharedHostPublishRoot ) ;
2016-03-14 15:40:19 -07:00
}
2016-03-14 12:17:15 -07:00
Directory . CreateDirectory ( SharedHostPublishRoot ) ;
2016-03-14 14:41:30 -07:00
2016-03-08 15:22:33 -08:00
// corehost will be renamed to dotnet at some point and then this can be removed.
2016-03-14 14:41:30 -07:00
File . Copy (
Path . Combine ( Dirs . Corehost , $"{CoreHostBaseName}{Constants.ExeSuffix}" ) ,
Path . Combine ( SharedHostPublishRoot , $"dotnet{Constants.ExeSuffix}" ) ) ;
File . Copy (
Path . Combine ( Dirs . Corehost , $"{Constants.DynamicLibPrefix}{DotnetHostFxrBaseName}{Constants.DynamicLibSuffix}" ) ,
Path . Combine ( SharedHostPublishRoot , $"{Constants.DynamicLibPrefix}{DotnetHostFxrBaseName}{Constants.DynamicLibSuffix}" ) ) ;
2016-03-08 15:22:33 -08:00
c . BuildContext [ "SharedHostPublishRoot" ] = SharedHostPublishRoot ;
return c . Success ( ) ;
}
2016-03-08 19:32:31 -08:00
private static string GetVersionFromProjectJson ( string pathToProjectJson )
{
Regex r = new Regex ( $"\" { Regex . Escape ( SharedFrameworkName ) } \ "\\s*:\\s*\"(?'version'[^\"]*)\"" ) ;
foreach ( var line in File . ReadAllLines ( pathToProjectJson ) )
{
var m = r . Match ( line ) ;
if ( m . Success )
{
return m . Groups [ "version" ] . Value ;
}
}
2016-03-14 12:17:15 -07:00
throw new InvalidOperationException ( "Unable to match the version name from " + pathToProjectJson ) ;
2016-03-08 19:32:31 -08:00
}
2016-03-10 03:19:00 -08:00
2016-03-14 12:17:15 -07:00
[Target]
[Environment("CROSSGEN_SHAREDFRAMEWORK", "1", "true")]
public static BuildTargetResult CrossGenAllManagedAssemblies ( BuildTargetContext c )
2016-03-10 03:19:00 -08:00
{
2016-03-14 12:17:15 -07:00
string pathToAssemblies = c . BuildContext . Get < string > ( "SharedFrameworkNameAndVersionRoot" ) ;
2016-03-10 03:19:00 -08:00
foreach ( var file in Directory . GetFiles ( pathToAssemblies ) )
{
string fileName = Path . GetFileName ( file ) ;
if ( fileName = = "mscorlib.dll" | | fileName = = "mscorlib.ni.dll" | | ! HasMetadata ( file ) )
{
continue ;
}
string tempPathName = Path . ChangeExtension ( file , "readytorun" ) ;
// This is not always correct. The version of crossgen we need to pick up is whatever one was restored as part
// of the Microsoft.NETCore.Runtime.CoreCLR package that is part of the shared library. For now, the version hardcoded
// in CompileTargets and the one in the shared library project.json match and are updated in lock step, but long term
// we need to be able to look at the project.lock.json file and figure out what version of Microsoft.NETCore.Runtime.CoreCLR
// was used, and then select that version.
ExecSilent ( Crossgen . GetCrossgenPathForVersion ( CompileTargets . CoreCLRVersion ) ,
"-readytorun" , "-in" , file , "-out" , tempPathName , "-platform_assemblies_paths" , pathToAssemblies ) ;
File . Delete ( file ) ;
File . Move ( tempPathName , file ) ;
}
2016-03-14 12:17:15 -07:00
return c . Success ( ) ;
2016-03-10 03:19:00 -08:00
}
private static bool HasMetadata ( string pathToFile )
{
try
{
using ( var inStream = File . OpenRead ( pathToFile ) )
{
using ( var peReader = new PEReader ( inStream ) )
{
return peReader . HasMetadata ;
}
}
} catch ( BadImageFormatException ) { }
return false ;
}
2016-03-08 15:22:33 -08:00
}
2016-03-10 03:19:00 -08:00
}