2017-10-05 04:44:58 +02:00
using System ;
using System.Collections.Generic ;
using System.IO ;
2019-11-30 23:30:15 +01:00
using System.Linq ;
2017-10-05 04:44:58 +02:00
using System.Threading.Tasks ;
2017-11-04 23:17:04 +01:00
using ElectronNET.CLI.Commands.Actions ;
2017-10-05 04:44:58 +02:00
namespace ElectronNET.CLI.Commands
{
public class BuildCommand : ICommand
{
public const string COMMAND_NAME = "build" ;
public const string COMMAND_DESCRIPTION = "Build your Electron Application." ;
2018-02-19 22:07:07 +01:00
public static string COMMAND_ARGUMENTS = "Needed: '/target' with params 'win/osx/linux' to build for a typical app or use 'custom' and specify .NET Core build config & electron build config" + Environment . NewLine +
" for custom target, check .NET Core RID Catalog and Electron build target/" + Environment . NewLine +
2021-07-02 12:47:51 +02:00
" e.g. '/target win' or '/target custom \"win7-x86;win\"'" + Environment . NewLine +
2019-01-15 22:21:25 +01:00
"Optional: '/dotnet-configuration' with the desired .NET Core build config e.g. release or debug. Default = Release" + Environment . NewLine +
2018-02-19 22:07:07 +01:00
"Optional: '/electron-arch' to specify the resulting electron processor architecture (e.g. ia86 for x86 builds). Be aware to use the '/target custom' param as well!" + Environment . NewLine +
"Optional: '/electron-params' specify any other valid parameter, which will be routed to the electron-packager." + Environment . NewLine +
2019-03-28 14:42:21 -07:00
"Optional: '/relative-path' to specify output a subdirectory for output." + Environment . NewLine +
"Optional: '/absolute-path to specify and absolute path for output." + Environment . NewLine +
"Optional: '/package-json' to specify a custom package.json file." + Environment . NewLine +
2020-11-12 11:15:37 +00:00
"Optional: '/install-modules' to force node module install. Implied by '/package-json'" + Environment . NewLine +
2021-03-04 14:45:52 +00:00
"Optional: '/Version' to specify the version that should be applied to both the `dotnet publish` and `electron-builder` commands. Implied by '/Version'" + Environment . NewLine +
2021-01-25 12:13:44 -06:00
"Optional: '/p:[property]' or '/property:[property]' to pass in dotnet publish properties. Example: '/property:Version=1.0.0' to override the FileVersion" + Environment . NewLine +
2018-02-19 22:07:07 +01:00
"Full example for a 32bit debug build with electron prune: build /target custom win7-x86;win32 /dotnet-configuration Debug /electron-arch ia32 /electron-params \"--prune=true \"" ;
2017-10-05 04:44:58 +02:00
public static IList < CommandOption > CommandOptions { get ; set ; } = new List < CommandOption > ( ) ;
2017-10-17 23:15:05 +02:00
private string [ ] _args ;
public BuildCommand ( string [ ] args )
{
_args = args ;
}
2018-02-12 21:17:40 +01:00
private string _paramTarget = "target" ;
private string _paramDotNetConfig = "dotnet-configuration" ;
private string _paramElectronArch = "electron-arch" ;
private string _paramElectronParams = "electron-params" ;
2019-03-28 14:42:21 -07:00
private string _paramOutputDirectory = "relative-path" ;
private string _paramAbsoluteOutput = "absolute-path" ;
private string _paramPackageJson = "package-json" ;
2019-03-29 19:02:34 -07:00
private string _paramForceNodeInstall = "install-modules" ;
2019-11-30 23:30:15 +01:00
private string _manifest = "manifest" ;
2020-05-10 00:31:28 +02:00
private string _paramPublishReadyToRun = "PublishReadyToRun" ;
2020-11-12 11:15:37 +00:00
private string _paramPublishSingleFile = "PublishSingleFile" ;
2021-03-04 14:45:52 +00:00
private string _paramVersion = "Version" ;
2018-02-12 21:17:40 +01:00
2017-10-05 04:44:58 +02:00
public Task < bool > ExecuteAsync ( )
{
return Task . Run ( ( ) = >
{
2017-10-08 23:22:20 +02:00
Console . WriteLine ( "Build Electron Application..." ) ;
2017-10-05 04:44:58 +02:00
2018-02-11 22:24:12 +01:00
SimpleCommandLineParser parser = new SimpleCommandLineParser ( ) ;
parser . Parse ( _args ) ;
2021-03-04 14:45:52 +00:00
//This version will be shared between the dotnet publish and electron-builder commands
string version = null ;
if ( parser . Arguments . ContainsKey ( _paramVersion ) )
version = parser . Arguments [ _paramVersion ] [ 0 ] ;
2019-11-16 15:13:02 +00:00
if ( ! parser . Arguments . ContainsKey ( _paramTarget ) )
{
Console . WriteLine ( $"Error: missing '{_paramTarget}' argument." ) ;
Console . WriteLine ( COMMAND_ARGUMENTS ) ;
return false ;
}
2018-02-12 21:17:40 +01:00
var desiredPlatform = parser . Arguments [ _paramTarget ] [ 0 ] ;
2018-02-11 22:24:12 +01:00
string specifiedFromCustom = string . Empty ;
2018-02-12 21:17:40 +01:00
if ( desiredPlatform = = "custom" & & parser . Arguments [ _paramTarget ] . Length > 1 )
2018-02-11 22:24:12 +01:00
{
2018-04-19 21:14:00 +02:00
specifiedFromCustom = parser . Arguments [ _paramTarget ] [ 1 ] ;
2018-02-11 22:24:12 +01:00
}
2018-02-12 21:17:40 +01:00
string configuration = "Release" ;
if ( parser . Arguments . ContainsKey ( _paramDotNetConfig ) )
{
configuration = parser . Arguments [ _paramDotNetConfig ] [ 0 ] ;
}
2018-02-11 22:24:12 +01:00
var platformInfo = GetTargetPlatformInformation . Do ( desiredPlatform , specifiedFromCustom ) ;
2017-10-17 23:15:05 +02:00
2018-01-24 22:39:46 +01:00
Console . WriteLine ( $"Build ASP.NET Core App for {platformInfo.NetCorePublishRid}..." ) ;
2017-11-04 23:17:04 +01:00
string tempPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "obj" , "desktop" , desiredPlatform ) ;
2020-11-12 11:15:37 +00:00
2017-11-04 23:17:04 +01:00
if ( Directory . Exists ( tempPath ) = = false )
2017-10-08 23:22:20 +02:00
{
2017-11-04 23:17:04 +01:00
Directory . CreateDirectory ( tempPath ) ;
2020-11-12 11:15:37 +00:00
}
2019-11-30 23:30:15 +01:00
else
{
Directory . Delete ( tempPath , true ) ;
Directory . CreateDirectory ( tempPath ) ;
2017-10-08 23:22:20 +02:00
}
2020-11-12 11:15:37 +00:00
2017-10-05 04:44:58 +02:00
2017-10-19 21:31:32 +02:00
Console . WriteLine ( "Executing dotnet publish in this directory: " + tempPath ) ;
string tempBinPath = Path . Combine ( tempPath , "bin" ) ;
2018-02-12 21:17:40 +01:00
Console . WriteLine ( $"Build ASP.NET Core App for {platformInfo.NetCorePublishRid} under {configuration}-Configuration..." ) ;
2021-01-25 12:13:44 -06:00
var dotNetPublishFlags = GetDotNetPublishFlags ( parser ) ;
2017-10-11 23:29:58 +02:00
2021-01-25 12:13:44 -06:00
var command =
$"dotnet publish -r {platformInfo.NetCorePublishRid} -c \" { configuration } \ " --output \"{tempBinPath}\" {string.Join(' ', dotNetPublishFlags.Select(kvp => $" { kvp . Key } = { kvp . Value } "))} --self-contained" ;
// output the command
Console . ForegroundColor = ConsoleColor . Green ;
Console . WriteLine ( command ) ;
Console . ResetColor ( ) ;
2020-11-12 11:15:37 +00:00
2021-01-25 12:13:44 -06:00
var resultCode = ProcessHelper . CmdExecute ( command , Directory . GetCurrentDirectory ( ) ) ;
2017-11-15 23:42:15 +01:00
if ( resultCode ! = 0 )
{
2018-09-27 22:48:54 +02:00
Console . WriteLine ( "Error occurred during dotnet publish: " + resultCode ) ;
2017-11-15 23:42:15 +01:00
return false ;
}
2017-10-11 23:29:58 +02:00
2017-11-04 23:17:04 +01:00
DeployEmbeddedElectronFiles . Do ( tempPath ) ;
2019-01-15 22:21:25 +01:00
var nodeModulesDirPath = Path . Combine ( tempPath , "node_modules" ) ;
2017-10-07 01:32:19 +02:00
2019-03-28 14:42:21 -07:00
if ( parser . Arguments . ContainsKey ( _paramPackageJson ) )
{
Console . WriteLine ( "Copying custom package.json." ) ;
File . Copy ( parser . Arguments [ _paramPackageJson ] [ 0 ] , Path . Combine ( tempPath , "package.json" ) , true ) ;
}
2017-11-15 22:58:33 +01:00
var checkForNodeModulesDirPath = Path . Combine ( tempPath , "node_modules" ) ;
2017-10-05 04:44:58 +02:00
2019-03-29 19:02:34 -07:00
if ( Directory . Exists ( checkForNodeModulesDirPath ) = = false | | parser . Contains ( _paramForceNodeInstall ) | | parser . Contains ( _paramPackageJson ) )
2019-05-16 01:06:56 +02:00
2020-11-12 11:15:37 +00:00
Console . WriteLine ( "Start npm install..." ) ;
2019-05-15 23:56:53 +02:00
ProcessHelper . CmdExecute ( "npm install --production" , tempPath ) ;
2019-01-15 22:21:25 +01:00
2019-01-09 23:04:56 +01:00
Console . WriteLine ( "ElectronHostHook handling started..." ) ;
string electronhosthookDir = Path . Combine ( Directory . GetCurrentDirectory ( ) , "ElectronHostHook" ) ;
if ( Directory . Exists ( electronhosthookDir ) )
{
string hosthookDir = Path . Combine ( tempPath , "ElectronHostHook" ) ;
DirectoryCopy . Do ( electronhosthookDir , hosthookDir , true , new List < string > ( ) { "node_modules" } ) ;
2019-10-07 22:53:50 +02:00
Console . WriteLine ( "Start npm install for hosthooks..." ) ;
2019-10-14 22:44:30 +02:00
ProcessHelper . CmdExecute ( "npm install" , hosthookDir ) ;
2019-01-09 23:04:56 +01:00
// ToDo: Not sure if this runs under linux/macos
2019-10-22 23:59:47 -04:00
ProcessHelper . CmdExecute ( @"npx tsc -p . --sourceMap false" , hosthookDir ) ;
2017-10-08 23:56:04 +02:00
}
2017-10-05 04:44:58 +02:00
2017-10-05 21:28:47 +02:00
Console . WriteLine ( "Build Electron Desktop Application..." ) ;
2019-03-28 14:42:21 -07:00
// Specifying an absolute path supercedes a relative path
2017-10-19 21:58:50 +02:00
string buildPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "bin" , "desktop" ) ;
2019-03-28 14:42:21 -07:00
if ( parser . Arguments . ContainsKey ( _paramAbsoluteOutput ) )
{
buildPath = parser . Arguments [ _paramAbsoluteOutput ] [ 0 ] ;
}
else if ( parser . Arguments . ContainsKey ( _paramOutputDirectory ) )
{
2020-11-12 11:15:37 +00:00
buildPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , parser . Arguments [ _paramOutputDirectory ] [ 0 ] ) ;
2019-03-28 14:42:21 -07:00
}
2017-10-19 21:31:32 +02:00
2017-10-05 23:37:52 +02:00
Console . WriteLine ( "Executing electron magic in this directory: " + buildPath ) ;
2018-02-12 21:17:40 +01:00
string electronArch = "x64" ;
if ( parser . Arguments . ContainsKey ( _paramElectronArch ) )
{
electronArch = parser . Arguments [ _paramElectronArch ] [ 0 ] ;
}
string electronParams = "" ;
if ( parser . Arguments . ContainsKey ( _paramElectronParams ) )
{
2018-02-12 21:23:23 +01:00
electronParams = parser . Arguments [ _paramElectronParams ] [ 0 ] ;
2018-02-12 21:17:40 +01:00
}
2019-05-18 02:01:06 +02:00
// ToDo: Make the same thing easer with native c# - we can save a tmp file in production code :)
Console . WriteLine ( "Create electron-builder configuration file..." ) ;
2019-11-30 23:30:15 +01:00
string manifestFileName = "electron.manifest.json" ;
2020-11-12 11:15:37 +00:00
if ( parser . Arguments . ContainsKey ( _manifest ) )
2019-11-30 23:30:15 +01:00
{
manifestFileName = parser . Arguments [ _manifest ] . First ( ) ;
}
2021-03-04 14:45:52 +00:00
ProcessHelper . CmdExecute (
string . IsNullOrWhiteSpace ( version )
? $"node build-helper.js {manifestFileName}"
: $"node build-helper.js {manifestFileName} {version}" , tempPath ) ;
2019-05-18 02:01:06 +02:00
2017-11-04 23:17:04 +01:00
Console . WriteLine ( $"Package Electron App for Platform {platformInfo.ElectronPackerPlatform}..." ) ;
2021-07-02 02:04:23 +02:00
ProcessHelper . CmdExecute ( $"npx electron-builder --config=./bin/electron-builder.json --{platformInfo.ElectronPackerPlatform} --{electronArch} -c.electronVersion=13.1.5 {electronParams}" , tempPath ) ;
2017-10-11 23:29:58 +02:00
2017-10-16 22:58:52 +02:00
Console . WriteLine ( "... done" ) ;
2017-10-05 04:44:58 +02:00
return true ;
} ) ;
}
2021-01-25 12:13:44 -06:00
private Dictionary < string , string > GetDotNetPublishFlags ( SimpleCommandLineParser parser )
{
var dotNetPublishFlags = new Dictionary < string , string >
{
{ "/p:PublishReadyToRun" , parser . TryGet ( _paramPublishReadyToRun , out var rtr ) ? rtr [ 0 ] : "true" } ,
{ "/p:PublishSingleFile" , parser . TryGet ( _paramPublishSingleFile , out var psf ) ? psf [ 0 ] : "true" } ,
} ;
2021-07-02 16:07:31 +01:00
if ( parser . Arguments . ContainsKey ( _paramVersion ) )
{
if ( parser . Arguments . Keys . All ( key = > ! key . StartsWith ( "p:Version=" ) & & ! key . StartsWith ( "property:Version=" ) ) )
dotNetPublishFlags . Add ( "/p:Version" , parser . Arguments [ _paramVersion ] [ 0 ] ) ;
if ( parser . Arguments . Keys . All ( key = > ! key . StartsWith ( "p:ProductVersion=" ) & & ! key . StartsWith ( "property:ProductVersion=" ) ) )
dotNetPublishFlags . Add ( "/p:ProductVersion" , parser . Arguments [ _paramVersion ] [ 0 ] ) ;
}
2021-01-25 12:13:44 -06:00
foreach ( var parm in parser . Arguments . Keys . Where ( key = > key . StartsWith ( "p:" ) | | key . StartsWith ( "property:" ) ) )
{
var split = parm . IndexOf ( '=' ) ;
if ( split < 0 )
{
continue ;
}
var key = $"/{parm.Substring(0, split)}" ;
// normalize the key
if ( key . StartsWith ( "/property:" ) )
{
key = key . Replace ( "/property:" , "/p:" ) ;
}
var value = parm . Substring ( split + 1 ) ;
if ( dotNetPublishFlags . ContainsKey ( key ) )
{
dotNetPublishFlags [ key ] = value ;
}
else
{
dotNetPublishFlags . Add ( key , value ) ;
}
}
return dotNetPublishFlags ;
}
2017-10-05 04:44:58 +02:00
}
2020-11-12 11:15:37 +00:00
}