mirror of
https://github.com/ElectronNET/Electron.NET.git
synced 2026-09-22 15:04:47 +00:00
Compare commits
28 Commits
0.5.2
...
0.6.0-pre.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
672ff6c40d | ||
|
|
7cd552a78f | ||
|
|
042d8e6c70 | ||
|
|
0140749753 | ||
|
|
7af2e954c7 | ||
|
|
da0e1f17db | ||
|
|
9cb210040d | ||
|
|
7e8f45b16d | ||
|
|
76dc687269 | ||
|
|
dcf4585510 | ||
|
|
a6d7434adf | ||
|
|
76ff44f67e | ||
|
|
20ab82b16d | ||
|
|
b27f705a6a | ||
|
|
8697c4de44 | ||
|
|
376a791e48 | ||
|
|
195a29052e | ||
|
|
f577a53227 | ||
|
|
382d544468 | ||
|
|
18f08f9aff | ||
|
|
def289ab54 | ||
|
|
4011f3d30b | ||
|
|
26b9d86ee4 | ||
|
|
3b57de976a | ||
|
|
d9e9b3d2e6 | ||
|
|
1ec1fde3c6 | ||
|
|
e5ccf75c9f | ||
|
|
819e643ab8 |
16
Changelog.md
16
Changelog.md
@@ -1,3 +1,19 @@
|
||||
# 0.6.0
|
||||
|
||||
## ElectronNET.Core
|
||||
|
||||
- Updated dependencies
|
||||
- Fixed single instance handling on macOS (#1040)
|
||||
- Fixed slow socket bridge startup by binding to an explicit loopback address (#1103)
|
||||
- Fixed socket bridge connection when a system proxy is configured (#1105)
|
||||
- Fixed electron-builder using the host RID instead of the target RID (#1097)
|
||||
- Fixed cross-compilation behavior on same platform (#1098) @epsnm
|
||||
- Fixed resolution of unrelated target (#1099) @epsnm
|
||||
- Added target framework customization (#1095) @epsnm
|
||||
- Added configurable Electron root directory for custom packaging layouts (#1106) @DYH1319
|
||||
- Added `ElectronExecutableName` to separate the product name from the executable name (#1003) @AeonSake
|
||||
- Added build extensibility properties `ElectronSkipExecCommands` and `ElectronIntermediatePublishDir` (#1106) @DYH1319
|
||||
|
||||
# 0.5.2
|
||||
|
||||
## ElectronNET.Core
|
||||
|
||||
@@ -5,4 +5,11 @@
|
||||
<!-- Uncomment to include parent Directory.Build.props file -->
|
||||
<!--<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />-->
|
||||
|
||||
<PropertyGroup>
|
||||
<ElectronNetBuildProps>$([MSBuild]::GetPathOfFileAbove('ElectronNet.Build.props', '$(MSBuildThisFileDirectory)../'))</ElectronNetBuildProps>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Only import specific project related MSBuild file -->
|
||||
<Import Project="$(ElectronNetBuildProps)" Condition=" '$(ElectronNetBuildProps)' != '' " />
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -54,7 +54,7 @@ Add the Electron.NET configuration to your `.csproj` file:
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.5.2" />
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.6.0" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
|
||||
@@ -28,9 +28,77 @@ These are the current default values when you don't make any changes:
|
||||
<ElectronPackageId>$(MSBuildProjectName.Replace(".", "-").ToLower())</ElectronPackageId>
|
||||
<ElectronBuilderJson>electron-builder.json</ElectronBuilderJson>
|
||||
<Title>$(MSBuildProjectName)</Title>
|
||||
<ElectronExecutableName></ElectronExecutableName>
|
||||
<ElectronRootDir>../..</ElectronRootDir>
|
||||
<ElectronSkipExecCommands>false</ElectronSkipExecCommands>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
### Product Name and Executable Name
|
||||
|
||||
`Title` is the product name of the application. It is used for the window title, the installer, the macOS app bundle and the Linux desktop entry, and it may contain spaces and other characters that are awkward in a file name.
|
||||
|
||||
`ElectronExecutableName` controls the name of the executable inside the package (electron-builder's `executableName`). When it is not set, it defaults to the following behavior:
|
||||
|
||||
| Target | Default |
|
||||
| --- | --- |
|
||||
| Windows | `$(Title)` |
|
||||
| Linux / macOS | `$(ElectronPackageId)` |
|
||||
|
||||
So to ship a product called `My Great App` with a `mygreatapp` binary on every platform:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<Title>My Great App</Title>
|
||||
<ElectronExecutableName>mygreatapp</ElectronExecutableName>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> `ElectronExecutable` is a separate, advanced setting: it is the binary Electron.NET launches when the .NET app starts first. It defaults to `ElectronExecutableName` and only needs to be changed when the package contains a launcher stub (for example added by an electron-builder hook) that should be started instead of the Electron binary.
|
||||
|
||||
### Custom Packaging Layout
|
||||
|
||||
`ElectronRootDir` tells the .NET app where to find the Electron binary when it is started first (DotNet-First startup of a packaged app). The path is resolved relative to the directory of the .NET executable; absolute paths are used as-is.
|
||||
|
||||
The default `../..` matches the standard Electron-First packaging layout produced by `dotnet publish`, where the .NET app is placed in `resources/bin` of the Electron package:
|
||||
|
||||
```
|
||||
<install-root>/
|
||||
MyApp # Electron executable
|
||||
resources/
|
||||
bin/
|
||||
MyApp # .NET executable
|
||||
```
|
||||
|
||||
If your own packaging pipeline places the .NET executable at the package root and Electron in a subdirectory, set `ElectronRootDir` accordingly:
|
||||
|
||||
```xml
|
||||
<ElectronRootDir>electron</ElectronRootDir>
|
||||
```
|
||||
|
||||
```
|
||||
<install-root>/
|
||||
MyApp # .NET executable
|
||||
electron/
|
||||
MyApp # Electron executable
|
||||
resources/
|
||||
locales/
|
||||
```
|
||||
|
||||
The property is also used to detect whether the app runs packaged or unpackaged, so it must point to the directory that actually contains the Electron binary.
|
||||
|
||||
### Build Extensibility
|
||||
|
||||
These properties are useful when Electron.NET is embedded into a custom build or CI pipeline:
|
||||
|
||||
| Property | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `ElectronSkipExecCommands` | `false` | Skips all `npm`/`npx` invocations of the Electron build and publish targets. Use it when the dependencies and the Electron distribution are provided by the pipeline itself (for example from a cache or an offline mirror). |
|
||||
| `ElectronIntermediatePublishDir` | `$(IntermediateOutputPath)PubTmp\` | Overrides the intermediate directory the .NET app is published to before it is assembled into the Electron package. |
|
||||
|
||||
`ElectronPackageId` and `Title` only fall back to the project name when they are not already set, so both can be defined by a `Directory.Build.props` or passed on the command line.
|
||||
|
||||
### Relation to package.json
|
||||
|
||||
ElectronNET.Core does not work with an `electron-manifest.json` file anymore.
|
||||
@@ -42,11 +110,17 @@ Since electron builder still expects a `package.json` file to exist, ElectronNET
|
||||
"productName": "$(ElectronTitle)",
|
||||
"build": {
|
||||
"appId": "$(ElectronPackageId)",
|
||||
"win": {
|
||||
"executableName": "$(ElectronExecutableName)"
|
||||
},
|
||||
"mac": {
|
||||
"executableName": "$(ElectronExecutableName)"
|
||||
},
|
||||
"linux": {
|
||||
"desktop": {
|
||||
"entry": { "Name": "$(Title)" }
|
||||
},
|
||||
"executableName": "$(ElectronPackageId)"
|
||||
"executableName": "$(ElectronExecutableName)"
|
||||
},
|
||||
"deb": {
|
||||
"desktop": {
|
||||
|
||||
@@ -222,6 +222,15 @@ The publish process will:
|
||||
> macOS builds can't be created on Windows machines because they require symlinks that aren't supported on Windows (per [this Electron issue](https://github.com/electron-userland/electron-packager/issues/71)). macOS builds can be produced on either Linux or macOS machines.
|
||||
|
||||
|
||||
## Custom Packaging Pipelines
|
||||
|
||||
The default layout is Electron-First: electron-builder produces the Electron package and the .NET app is placed in `resources/bin`. If you assemble the package yourself and put the .NET executable at the package root with Electron in a subdirectory, set `ElectronRootDir` so the .NET app can locate the Electron binary at runtime.
|
||||
|
||||
For pipelines that provide the npm dependencies themselves, `ElectronSkipExecCommands` suppresses all `npm`/`npx` invocations, and `ElectronIntermediatePublishDir` redirects the intermediate publish output.
|
||||
|
||||
See [Configuration](Configuration.md#custom-packaging-layout) for details.
|
||||
|
||||
|
||||
## 🚀 Next Steps
|
||||
|
||||
- **[Startup Methods](Startup-Methods.md)** - Understanding different launch modes for packaged apps
|
||||
|
||||
@@ -157,10 +157,19 @@ namespace ElectronNET.API
|
||||
}
|
||||
|
||||
BridgeConnector.Socket.On<int[]>("BrowserWindowClosed", HandleBrowserWindowClosed);
|
||||
BridgeConnector.Socket.On<int>("BrowserWindowRecreated", HandleBrowserWindowRecreated);
|
||||
_browserWindowClosedSubscribed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleBrowserWindowRecreated(int id)
|
||||
{
|
||||
if (_browserWindows.All(window => window.Id != id))
|
||||
{
|
||||
_browserWindows.Add(new BrowserWindow(id));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleBrowserWindowClosed(int[] ids)
|
||||
{
|
||||
if (ids == null || ids.Length == 0)
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace ElectronNET.API;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using ElectronNET.API.Serialization;
|
||||
using SocketIO.Serializer.SystemTextJson;
|
||||
@@ -18,13 +19,21 @@ internal class SocketIOConnection : ISocketConnection
|
||||
|
||||
public SocketIOConnection(string uri, string authorization)
|
||||
{
|
||||
var opts = string.IsNullOrEmpty(authorization) ? new SocketIOOptions() : new SocketIOOptions
|
||||
var opts = new SocketIOOptions
|
||||
{
|
||||
ExtraHeaders = new Dictionary<string, string>
|
||||
// The bridge is a loopback IPC channel, so it must never go through a
|
||||
// (system) proxy - otherwise the handshake never reaches the socket server.
|
||||
Proxy = DirectProxy.Instance,
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(authorization))
|
||||
{
|
||||
opts.ExtraHeaders = new Dictionary<string, string>
|
||||
{
|
||||
["authorization"] = authorization
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
_socket = new SocketIO(uri, opts);
|
||||
_socket.Serializer = new SystemTextJsonSerializer(ElectronJson.Options);
|
||||
// Use default System.Text.Json serializer from SocketIOClient.
|
||||
@@ -152,4 +161,15 @@ internal class SocketIOConnection : ISocketConnection
|
||||
throw new ObjectDisposedException(nameof(SocketIOConnection));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DirectProxy : IWebProxy
|
||||
{
|
||||
public static readonly DirectProxy Instance = new DirectProxy();
|
||||
|
||||
public ICredentials Credentials { get; set; }
|
||||
|
||||
public Uri GetProxy(Uri destination) => destination;
|
||||
|
||||
public bool IsBypassed(Uri host) => true;
|
||||
}
|
||||
}
|
||||
@@ -37,10 +37,18 @@
|
||||
public ProcessRunner(string name)
|
||||
{
|
||||
this.Name = name;
|
||||
this.ExitWaitTimeout = DefaultExitWaitTimeout;
|
||||
}
|
||||
|
||||
public event EventHandler<EventArgs> ProcessExited;
|
||||
|
||||
/// <summary>Gets or sets the value <see cref="ExitWaitTimeout"/> is initialized with.</summary>
|
||||
public static int DefaultExitWaitTimeout { get; set; } = 5000;
|
||||
|
||||
/// <summary>Gets or sets the time in milliseconds to wait for the process and its I/O to finish
|
||||
/// after the exited event has been raised. Values of zero or less wait indefinitely.</summary>
|
||||
public int ExitWaitTimeout { get; set; }
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
private Process Process
|
||||
@@ -498,8 +506,17 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
var timeout = this.ExitWaitTimeout;
|
||||
|
||||
// This shouldn't throw here, but the mono process implementation doesn't always behave as it should.
|
||||
this.process.WaitForExit();
|
||||
if (timeout > 0)
|
||||
{
|
||||
this.process.WaitForExit(timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.process.WaitForExit();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<Import Project="..\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
|
||||
<TargetFrameworks>$(ElectronNetTargetFrameworks)</TargetFrameworks>
|
||||
<PackageOutputPath>..\..\artifacts</PackageOutputPath>
|
||||
<PackageId>$(PackageNamePrefix).API</PackageId>
|
||||
<Title>$(PackageId)</Title>
|
||||
@@ -29,7 +29,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="SocketIOClient" Version="3.1.2" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="8.0.22" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.6" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.6" Condition=" '$(TargetFramework)' == 'net6.0' " />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
internal const int DefaultSocketPort = 8000;
|
||||
internal const int DefaultWebPort = 8001;
|
||||
internal const string ElectronPortArgumentName = "electronPort";
|
||||
internal const string ElectronHostArgumentName = "electronHost";
|
||||
internal const string ElectronPidArgumentName = "electronPID";
|
||||
internal const string ElectronAuthTokenArgumentName = "electronAuthToken";
|
||||
|
||||
@@ -31,6 +32,11 @@
|
||||
|
||||
public static int? ElectronSocketPort { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The loopback address the Electron socket bridge is listening on.
|
||||
/// </summary>
|
||||
public static string ElectronSocketHost { get; internal set; }
|
||||
|
||||
public static int? AspNetWebPort { get; internal set; }
|
||||
|
||||
public static StartupMethod StartupMethod { get; internal set; }
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
public string ElectronVersion { get; internal set; }
|
||||
|
||||
public string ElectronRootDir { get; internal set; }
|
||||
|
||||
public string RuntimeIdentifier { get; internal set; }
|
||||
|
||||
public bool ElectronSingleInstance { get; internal set; }
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace ElectronNET.Runtime.Helpers
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the directory that contains the Electron binary of a packaged application.
|
||||
/// </summary>
|
||||
internal static class ElectronRootDirResolver
|
||||
{
|
||||
/// <summary>The location of the Electron binary relative to the .NET application directory in the
|
||||
/// default (Electron-first) layout, where the .NET application is placed in 'resources/bin'.</summary>
|
||||
internal const string DefaultElectronRootDir = "../..";
|
||||
|
||||
/// <summary>Resolves the Electron root directory relative to the given .NET application directory.</summary>
|
||||
/// <param name="baseDirectory">The directory containing the .NET application.</param>
|
||||
/// <returns>The directory containing the Electron binary.</returns>
|
||||
public static DirectoryInfo Resolve(DirectoryInfo baseDirectory)
|
||||
{
|
||||
var rootDir = ElectronNetRuntime.BuildInfo?.ElectronRootDir;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(rootDir))
|
||||
{
|
||||
rootDir = DefaultElectronRootDir;
|
||||
}
|
||||
|
||||
// An absolute ElectronRootDir is used as-is by Path.Combine.
|
||||
return new DirectoryInfo(Path.GetFullPath(Path.Combine(baseDirectory.FullName, rootDir)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,15 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
// Packaged, if the Electron binary is found where ElectronRootDir points to.
|
||||
var electronExecutable = ElectronNetRuntime.ElectronExecutable;
|
||||
|
||||
if (!string.IsNullOrEmpty(electronExecutable) &&
|
||||
File.Exists(Path.Combine(ElectronRootDirResolver.Resolve(dir).FullName, electronExecutable)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dir.GetDirectories().Any(e => e.Name == ".electron"))
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using ElectronNET.Common;
|
||||
using ElectronNET.Runtime.Data;
|
||||
using ElectronNET.Runtime.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Launches and manages the Electron app process.
|
||||
@@ -18,7 +19,7 @@
|
||||
[Localizable(false)]
|
||||
internal class ElectronProcessActive : ElectronProcessBase
|
||||
{
|
||||
private readonly Regex extractor = new Regex("^Electron Socket: listening on port (\\d+) at .* using ([a-f0-9]+)$");
|
||||
private readonly Regex extractor = new Regex("^Electron Socket: listening on port (\\d+) at (\\S+) using ([a-f0-9]+)$");
|
||||
|
||||
private readonly bool isUnpackaged;
|
||||
private readonly string electronBinaryName;
|
||||
@@ -87,7 +88,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
dir = dir.Parent!.Parent!;
|
||||
dir = ElectronRootDirResolver.Resolve(dir);
|
||||
startCmd = Path.Combine(dir.FullName, this.electronBinaryName);
|
||||
args = $"-dotnetpacked -electronforcedport={this.socketPort:D} " + this.extraArguments;
|
||||
workingDir = dir.FullName;
|
||||
@@ -179,11 +180,13 @@
|
||||
if (match?.Success ?? false)
|
||||
{
|
||||
var port = int.Parse(match.Groups[1].Value);
|
||||
var token = match.Groups[2].Value;
|
||||
var host = match.Groups[2].Value;
|
||||
var token = match.Groups[3].Value;
|
||||
|
||||
this.process.LineReceived -= Read_SocketIO_Parameters;
|
||||
ElectronNetRuntime.ElectronAuthToken = token;
|
||||
ElectronNetRuntime.ElectronSocketPort = port;
|
||||
ElectronNetRuntime.ElectronSocketHost = host;
|
||||
tcs.SetResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,19 @@
|
||||
{
|
||||
this.socketPort = socketPort;
|
||||
this.authorization = authorization;
|
||||
this.socketUrl = $"http://localhost:{this.socketPort}";
|
||||
this.socketUrl = $"http://{FormatHost(ElectronNetRuntime.ElectronSocketHost)}:{this.socketPort}";
|
||||
}
|
||||
|
||||
// The Electron host reports the loopback address it is actually listening on; only
|
||||
// when it is unknown we have to fall back to the ambiguous hostname.
|
||||
private static string FormatHost(string socketHost)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(socketHost))
|
||||
{
|
||||
return "localhost";
|
||||
}
|
||||
|
||||
return socketHost.Contains(':') ? $"[{socketHost}]" : socketHost;
|
||||
}
|
||||
|
||||
public int SocketPort => this.socketPort;
|
||||
|
||||
@@ -94,8 +94,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
var pidArg = argsList.FirstOrDefault(e => e.Contains(ElectronNetRuntime.ElectronPidArgumentName, StringComparison.OrdinalIgnoreCase));
|
||||
var hostArg = argsList.FirstOrDefault(e => e.Contains(ElectronNetRuntime.ElectronHostArgumentName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (hostArg != null)
|
||||
{
|
||||
var parts = hostArg.Split('=', StringSplitOptions.TrimEntries);
|
||||
|
||||
if (parts.Length > 1 && !string.IsNullOrWhiteSpace(parts[1]))
|
||||
{
|
||||
ElectronNetRuntime.ElectronSocketHost = parts[1];
|
||||
|
||||
Console.WriteLine("Use Electron Host: " + parts[1]);
|
||||
}
|
||||
}
|
||||
var pidArg = argsList.FirstOrDefault(e => e.Contains(ElectronNetRuntime.ElectronPidArgumentName, StringComparison.OrdinalIgnoreCase));
|
||||
if (pidArg != null)
|
||||
{
|
||||
var parts = pidArg.Split('=', StringSplitOptions.TrimEntries);
|
||||
@@ -166,6 +178,7 @@
|
||||
{
|
||||
buildInfo.ElectronExecutable = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.ElectronExecutable))?.Value;
|
||||
buildInfo.ElectronVersion = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.ElectronVersion))?.Value;
|
||||
buildInfo.ElectronRootDir = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.ElectronRootDir))?.Value;
|
||||
buildInfo.RuntimeIdentifier = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.RuntimeIdentifier))?.Value;
|
||||
buildInfo.Title = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.Title))?.Value;
|
||||
buildInfo.Version = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.Version))?.Value;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<Import Project="..\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
|
||||
<TargetFrameworks>$(ElectronNetTargetFrameworks)</TargetFrameworks>
|
||||
<PackageOutputPath>..\..\artifacts</PackageOutputPath>
|
||||
<PackageId>$(PackageNamePrefix).AspNet</PackageId>
|
||||
<Title>$(PackageId)</Title>
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<ProjectReference Include="..\ElectronNET.API\ElectronNET.API.csproj" Condition="$(ElectronNetDevMode)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.5.2" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.6.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\ElectronNET\build\ElectronNET.Core.targets" Condition="$(ElectronNetDevMode)" />
|
||||
|
||||
@@ -247,9 +247,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.6",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz",
|
||||
"integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==",
|
||||
"version": "4.2.7",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz",
|
||||
"integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.3864779">
|
||||
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.6537497">
|
||||
<ItemGroup>
|
||||
<None Include=".vscode\tasks.json" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -39,7 +39,6 @@ const windows = (global["browserWindows"] =
|
||||
global["browserWindows"] || []);
|
||||
let readyToShowWindowsIds = [];
|
||||
let window;
|
||||
let lastOptions;
|
||||
let electronSocket;
|
||||
const proxyToCredentialsMap = (global["proxyToCredentialsMap"] = global["proxyToCredentialsMap"] || []);
|
||||
module.exports = (socket, app) => {
|
||||
@@ -203,6 +202,18 @@ module.exports = (socket, app) => {
|
||||
});
|
||||
});
|
||||
socket.on("createBrowserWindow", (options, loadUrl) => {
|
||||
createWindow(options, loadUrl, "BrowserWindowCreated");
|
||||
});
|
||||
// Allows the host (main.js) to bring the main window back when the app is activated
|
||||
// again on macOS after all windows have been closed.
|
||||
global["recreateMainWindow"] = () => {
|
||||
if (electron_1.BrowserWindow.getAllWindows().length > 0 || !app["mainWindowOptions"]) {
|
||||
return false;
|
||||
}
|
||||
createWindow(app["mainWindowOptions"], app["mainWindowURL"], "BrowserWindowRecreated");
|
||||
return true;
|
||||
};
|
||||
function createWindow(options, loadUrl, createdEventName) {
|
||||
if (options.webPreferences &&
|
||||
!("nodeIntegration" in options.webPreferences)) {
|
||||
options = {
|
||||
@@ -226,21 +237,19 @@ module.exports = (socket, app) => {
|
||||
delete options.isRunningBlazor;
|
||||
// we dont want to recreate the window when watch is ready.
|
||||
if (app.commandLine.hasSwitch("watch") &&
|
||||
app["mainWindowURL"] === loadUrl) {
|
||||
app["mainWindowURL"] === loadUrl &&
|
||||
app["mainWindow"] &&
|
||||
!app["mainWindow"].isDestroyed()) {
|
||||
window = app["mainWindow"];
|
||||
if (window) {
|
||||
window.reload();
|
||||
synchronizeWindowRegistry();
|
||||
if (!windows.some((entry) => tryGetWindowId(entry) === window.id)) {
|
||||
windows.push(window);
|
||||
}
|
||||
electronSocket.emit("BrowserWindowCreated", window.id);
|
||||
return;
|
||||
window.reload();
|
||||
synchronizeWindowRegistry();
|
||||
if (!windows.some((entry) => tryGetWindowId(entry) === window.id)) {
|
||||
windows.push(window);
|
||||
}
|
||||
electronSocket.emit(createdEventName, window.id);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
window = new electron_1.BrowserWindow(options);
|
||||
}
|
||||
window = new electron_1.BrowserWindow(options);
|
||||
if (options.proxy) {
|
||||
window.webContents.session.setProxy({ proxyRules: options.proxy });
|
||||
}
|
||||
@@ -255,18 +264,10 @@ module.exports = (socket, app) => {
|
||||
readyToShowWindowsIds.push(window.id);
|
||||
}
|
||||
});
|
||||
lastOptions = options;
|
||||
window.on("closed", () => {
|
||||
synchronizeWindowRegistry();
|
||||
emitBrowserWindowClosed();
|
||||
});
|
||||
app.on("activate", () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (window === null && lastOptions) {
|
||||
window = new electron_1.BrowserWindow(lastOptions);
|
||||
}
|
||||
});
|
||||
if (loadUrl) {
|
||||
// Append authentication token to initial URL if available
|
||||
const token = global["authToken"];
|
||||
@@ -299,10 +300,14 @@ module.exports = (socket, app) => {
|
||||
if (app["mainWindowURL"] == undefined || app["mainWindowURL"] == "") {
|
||||
app["mainWindowURL"] = loadUrl;
|
||||
app["mainWindow"] = window;
|
||||
app["mainWindowOptions"] = options;
|
||||
}
|
||||
else if (app["mainWindowURL"] === loadUrl) {
|
||||
app["mainWindow"] = window;
|
||||
}
|
||||
windows.push(window);
|
||||
electronSocket.emit("BrowserWindowCreated", window.id);
|
||||
});
|
||||
electronSocket.emit(createdEventName, window.id);
|
||||
}
|
||||
socket.on("browserWindowDestroy", (id) => {
|
||||
getWindowById(id).destroy();
|
||||
});
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -10,7 +10,6 @@ const windows: Electron.BrowserWindow[] = (global["browserWindows"] =
|
||||
let readyToShowWindowsIds: number[] = [];
|
||||
|
||||
let window;
|
||||
let lastOptions;
|
||||
let electronSocket;
|
||||
|
||||
const proxyToCredentialsMap: { [proxy: string]: string } = (global[
|
||||
@@ -217,6 +216,26 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
});
|
||||
|
||||
socket.on("createBrowserWindow", (options, loadUrl) => {
|
||||
createWindow(options, loadUrl, "BrowserWindowCreated");
|
||||
});
|
||||
|
||||
// Allows the host (main.js) to bring the main window back when the app is activated
|
||||
// again on macOS after all windows have been closed.
|
||||
global["recreateMainWindow"] = () => {
|
||||
if (BrowserWindow.getAllWindows().length > 0 || !app["mainWindowOptions"]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
createWindow(
|
||||
app["mainWindowOptions"],
|
||||
app["mainWindowURL"],
|
||||
"BrowserWindowRecreated",
|
||||
);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
function createWindow(options, loadUrl, createdEventName: string) {
|
||||
if (
|
||||
options.webPreferences &&
|
||||
!("nodeIntegration" in options.webPreferences)
|
||||
@@ -250,22 +269,24 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
// we dont want to recreate the window when watch is ready.
|
||||
if (
|
||||
app.commandLine.hasSwitch("watch") &&
|
||||
app["mainWindowURL"] === loadUrl
|
||||
app["mainWindowURL"] === loadUrl &&
|
||||
app["mainWindow"] &&
|
||||
!app["mainWindow"].isDestroyed()
|
||||
) {
|
||||
window = app["mainWindow"];
|
||||
if (window) {
|
||||
window.reload();
|
||||
synchronizeWindowRegistry();
|
||||
if (!windows.some((entry) => tryGetWindowId(entry) === window.id)) {
|
||||
windows.push(window);
|
||||
}
|
||||
electronSocket.emit("BrowserWindowCreated", window.id);
|
||||
return;
|
||||
window.reload();
|
||||
synchronizeWindowRegistry();
|
||||
|
||||
if (!windows.some((entry) => tryGetWindowId(entry) === window.id)) {
|
||||
windows.push(window);
|
||||
}
|
||||
} else {
|
||||
window = new BrowserWindow(options);
|
||||
|
||||
electronSocket.emit(createdEventName, window.id);
|
||||
return;
|
||||
}
|
||||
|
||||
window = new BrowserWindow(options);
|
||||
|
||||
if (options.proxy) {
|
||||
window.webContents.session.setProxy({ proxyRules: options.proxy });
|
||||
}
|
||||
@@ -284,21 +305,11 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
}
|
||||
});
|
||||
|
||||
lastOptions = options;
|
||||
|
||||
window.on("closed", () => {
|
||||
synchronizeWindowRegistry();
|
||||
emitBrowserWindowClosed();
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (window === null && lastOptions) {
|
||||
window = new BrowserWindow(lastOptions);
|
||||
}
|
||||
});
|
||||
|
||||
if (loadUrl) {
|
||||
// Append authentication token to initial URL if available
|
||||
const token = global["authToken"];
|
||||
@@ -338,11 +349,14 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
if (app["mainWindowURL"] == undefined || app["mainWindowURL"] == "") {
|
||||
app["mainWindowURL"] = loadUrl;
|
||||
app["mainWindow"] = window;
|
||||
app["mainWindowOptions"] = options;
|
||||
} else if (app["mainWindowURL"] === loadUrl) {
|
||||
app["mainWindow"] = window;
|
||||
}
|
||||
|
||||
windows.push(window);
|
||||
electronSocket.emit("BrowserWindowCreated", window.id);
|
||||
});
|
||||
electronSocket.emit(createdEventName, window.id);
|
||||
}
|
||||
|
||||
socket.on("browserWindowDestroy", (id) => {
|
||||
getWindowById(id).destroy();
|
||||
|
||||
@@ -103,9 +103,44 @@ app.on('will-finish-launching', () => {
|
||||
|
||||
const manifestJsonFile = require(manifestJsonFilePath);
|
||||
|
||||
// Brings the app back to the foreground: focuses an already existing window or - if
|
||||
// all windows have been closed (which keeps the app alive on macOS) - recreates the
|
||||
// main window.
|
||||
function activateApp() {
|
||||
const windows = BrowserWindow.getAllWindows().filter((window) => !window.isDestroyed());
|
||||
|
||||
if (!windows.length) {
|
||||
return typeof global.recreateMainWindow === 'function' && global.recreateMainWindow();
|
||||
}
|
||||
|
||||
const target = windows.find((window) => window.isVisible()) || windows[0];
|
||||
|
||||
if (target.isMinimized()) {
|
||||
target.restore();
|
||||
}
|
||||
|
||||
if (!target.isVisible()) {
|
||||
target.show();
|
||||
}
|
||||
|
||||
target.focus();
|
||||
|
||||
if (platform() === 'darwin') {
|
||||
// On macOS focusing a window does not necessarily bring the app itself to the front
|
||||
app.focus({ steal: true });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (manifestJsonFile.singleInstance) {
|
||||
const mainInstance = app.requestSingleInstanceLock();
|
||||
app.on('second-instance', (events, args = []) => {
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
// Another instance already owns the lock. Exit right away so that neither the
|
||||
// socket bridge nor the .NET backend process of this instance is started.
|
||||
app.exit(0);
|
||||
}
|
||||
|
||||
app.on('second-instance', (event, args = []) => {
|
||||
args.forEach((parameter) => {
|
||||
const words = parameter.split('=');
|
||||
|
||||
@@ -116,20 +151,16 @@ if (manifestJsonFile.singleInstance) {
|
||||
}
|
||||
});
|
||||
|
||||
const windows = BrowserWindow.getAllWindows();
|
||||
if (windows.length) {
|
||||
if (windows[0].isMinimized()) {
|
||||
windows[0].restore();
|
||||
}
|
||||
windows[0].focus();
|
||||
}
|
||||
activateApp();
|
||||
});
|
||||
|
||||
if (!mainInstance) {
|
||||
app.quit();
|
||||
}
|
||||
}
|
||||
|
||||
// On macOS launching an already running app (or clicking its dock icon) does not
|
||||
// start a second instance - the 'activate' event is raised instead.
|
||||
app.on('activate', () => {
|
||||
activateApp();
|
||||
});
|
||||
|
||||
// Collect user supplied command line args (excluding those handled by Electron host itself)
|
||||
function getForwardedArgs() {
|
||||
const skipSwitches = new Set(['unpackedelectron', 'unpackeddotnet', 'dotnetpacked']);
|
||||
@@ -144,6 +175,7 @@ function getForwardedArgs() {
|
||||
if (cleaned.startsWith('remote-debugging-port')) return false;
|
||||
// We add /electronPort ourselves later
|
||||
if (cleaned.startsWith('electronPort=')) return false;
|
||||
if (cleaned.startsWith('electronHost=')) return false;
|
||||
if (cleaned.startsWith('electronWebPort=')) return false;
|
||||
return true;
|
||||
});
|
||||
@@ -264,7 +296,6 @@ function startSocketApiBridge(port) {
|
||||
// otherwise the Windows Firewall will be triggered
|
||||
console.debug('Electron Socket: starting...');
|
||||
server = createServer();
|
||||
const host = !port ? '127.0.0.1' : 'localhost';
|
||||
let hostHook;
|
||||
io = new Server({
|
||||
pingTimeout: 60000, // in ms, default is 5000
|
||||
@@ -272,16 +303,35 @@ function startSocketApiBridge(port) {
|
||||
});
|
||||
io.attach(server);
|
||||
|
||||
server.listen(port, host);
|
||||
// Never bind to the 'localhost' hostname: it may resolve to ::1 and 127.0.0.1 in any
|
||||
// order, so server and client can end up on different stacks - which costs a failed
|
||||
// connection attempt (or a DNS lookup) on every startup.
|
||||
const hostCandidates = ['127.0.0.1', '::1'];
|
||||
let hostIndex = 0;
|
||||
|
||||
server.on('error', (error) => {
|
||||
const isUnavailable = error.code === 'EADDRNOTAVAIL' || error.code === 'EAFNOSUPPORT' || error.code === 'EINVAL';
|
||||
|
||||
if (isUnavailable && hostIndex + 1 < hostCandidates.length) {
|
||||
console.warn(`Electron Socket: cannot bind to ${hostCandidates[hostIndex]} (${error.code}), falling back to ${hostCandidates[hostIndex + 1]}.`);
|
||||
hostIndex++;
|
||||
server.listen(port, hostCandidates[hostIndex]);
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('Electron Socket: ' + error.message);
|
||||
});
|
||||
|
||||
server.listen(port, hostCandidates[hostIndex]);
|
||||
server.on('listening', function () {
|
||||
const addr = server.address();
|
||||
console.info(`Electron Socket: listening on port ${addr.port} at ${addr.address} using ${authToken}`);
|
||||
|
||||
// Now that socket connection is established, we can guarantee port will not be open for portscanner
|
||||
if (unpackedelectron) {
|
||||
startAspCoreBackendUnpackaged(addr.port);
|
||||
startAspCoreBackendUnpackaged(addr.port, addr.address);
|
||||
} else if (!unpackeddotnet && !dotnetpacked) {
|
||||
startAspCoreBackend(addr.port);
|
||||
startAspCoreBackend(addr.port, addr.address);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -385,7 +435,7 @@ function startSocketApiBridge(port) {
|
||||
});
|
||||
}
|
||||
|
||||
function startAspCoreBackend(electronPort) {
|
||||
function startAspCoreBackend(electronPort, electronHost) {
|
||||
startBackend();
|
||||
|
||||
function startBackend() {
|
||||
@@ -394,6 +444,7 @@ function startAspCoreBackend(electronPort) {
|
||||
const parameters = [
|
||||
envParam,
|
||||
`/electronPort=${electronPort}`,
|
||||
`/electronHost=${electronHost}`,
|
||||
`/electronPID=${process.pid}`,
|
||||
`/electronAuthToken=${authToken}`,
|
||||
// forward user supplied args (avoid duplicate environment)
|
||||
@@ -416,7 +467,7 @@ function startAspCoreBackend(electronPort) {
|
||||
}
|
||||
}
|
||||
|
||||
function startAspCoreBackendUnpackaged(electronPort) {
|
||||
function startAspCoreBackendUnpackaged(electronPort, electronHost) {
|
||||
startBackend();
|
||||
|
||||
function startBackend() {
|
||||
@@ -425,6 +476,7 @@ function startAspCoreBackendUnpackaged(electronPort) {
|
||||
const parameters = [
|
||||
envParam,
|
||||
`/electronPort=${electronPort}`,
|
||||
`/electronHost=${electronHost}`,
|
||||
`/electronPID=${process.pid}`,
|
||||
`/electronAuthToken=${authToken}`,
|
||||
...forwardedArgs.filter(a => !(envParam && a.startsWith('--environment=')))
|
||||
|
||||
20
src/ElectronNET.Host/package-lock.json
generated
20
src/ElectronNET.Host/package-lock.json
generated
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.18",
|
||||
"electron": "^30.0.3",
|
||||
"electron": "^39.8.10",
|
||||
"eslint": "^9.39.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
@@ -762,15 +762,15 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "30.5.1",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-30.5.1.tgz",
|
||||
"integrity": "sha512-AhL7+mZ8Lg14iaNfoYTkXQ2qee8mmsQyllKdqxlpv/zrKgfxz6jNVtcRRbQtLxtF8yzcImWdfTQROpYiPumdbw==",
|
||||
"version": "39.8.10",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-39.8.10.tgz",
|
||||
"integrity": "sha512-zbYtGPYUI7PzqLAzkk21Rk6j67WN0hxn0Mq/njErZo1d0HSf33is4f8ICI5fMLy5vYe0JtCtM5sYunNOaochSQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@electron/get": "^2.0.0",
|
||||
"@types/node": "^20.9.0",
|
||||
"@types/node": "^22.7.7",
|
||||
"extract-zip": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
@@ -847,16 +847,6 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron/node_modules/@types/node": {
|
||||
"version": "20.19.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz",
|
||||
"integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.18",
|
||||
"electron": "^30.0.3",
|
||||
"electron": "^39.8.10",
|
||||
"eslint": "^9.39.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ElectronNET.IntegrationTests.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the Electron clean targets.
|
||||
/// Covers GitHub issue #1096: cleaning must not require a restore, so the clean targets
|
||||
/// must not depend on targets that need the assets file (NETSDK1004).
|
||||
/// </summary>
|
||||
public class ElectronCleanTargetsTests
|
||||
{
|
||||
private static readonly string CorePropsPath = FindBuildFile("src/ElectronNET/build/ElectronNET.Core.props", "ElectronNET/build/ElectronNET.Core.props");
|
||||
private static readonly string CoreTargetsPath = FindBuildFile("src/ElectronNET/build/ElectronNET.Core.targets", "ElectronNET/build/ElectronNET.Core.targets");
|
||||
|
||||
[Fact]
|
||||
public async Task ElectronCleanTargets_WithoutRestore_ShouldSucceed()
|
||||
{
|
||||
var tempDir = CreateTempProjectDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
await WriteMinimalCsprojAsync(tempDir);
|
||||
|
||||
var (exitCode, output) = await RunDotnetMsBuildAsync(tempDir, "Clean");
|
||||
|
||||
output.Should().NotContain("NETSDK1004",
|
||||
$"cleaning must not require the assets file of a previous restore. Full output:\n{output}");
|
||||
|
||||
exitCode.Should().Be(0,
|
||||
$"cleaning an unrestored project must succeed. Full output:\n{output}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(tempDir, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
private static string FindBuildFile(string relativeFromRepoRoot, string relativeFromSrc)
|
||||
{
|
||||
var dir = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (dir != null)
|
||||
{
|
||||
var fromRepoRoot = Path.Combine(dir.FullName, relativeFromRepoRoot);
|
||||
if (File.Exists(fromRepoRoot))
|
||||
{
|
||||
return Path.GetFullPath(fromRepoRoot);
|
||||
}
|
||||
|
||||
var fromSrc = Path.Combine(dir.FullName, relativeFromSrc);
|
||||
if (File.Exists(fromSrc))
|
||||
{
|
||||
return Path.GetFullPath(fromSrc);
|
||||
}
|
||||
|
||||
dir = dir.Parent;
|
||||
}
|
||||
|
||||
throw new FileNotFoundException(
|
||||
$"Could not locate '{relativeFromRepoRoot}' by walking up from '{AppContext.BaseDirectory}'.");
|
||||
}
|
||||
|
||||
private static string CreateTempProjectDirectory()
|
||||
{
|
||||
var tempDir = Path.Combine(Path.GetTempPath(), $"electron-net-clean-test-{Guid.NewGuid():N}");
|
||||
Directory.CreateDirectory(tempDir);
|
||||
Directory.CreateDirectory(Path.Combine(tempDir, "Properties"));
|
||||
return tempDir;
|
||||
}
|
||||
|
||||
private static Task WriteMinimalCsprojAsync(string tempDir)
|
||||
{
|
||||
var propsPathEscaped = CorePropsPath.Replace("'", "'");
|
||||
var targetsPathEscaped = CoreTargetsPath.Replace("'", "'");
|
||||
|
||||
return File.WriteAllTextAsync(
|
||||
Path.Combine(tempDir, "TestApp.csproj"),
|
||||
$$"""
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="{{propsPathEscaped}}" />
|
||||
|
||||
<Import Project="{{targetsPathEscaped}}" />
|
||||
</Project>
|
||||
""");
|
||||
}
|
||||
|
||||
private static async Task<(int ExitCode, string Output)> RunDotnetMsBuildAsync(string workingDirectory, string target)
|
||||
{
|
||||
// Deliberately without /restore - that is what issue #1096 is about.
|
||||
var psi = new ProcessStartInfo("dotnet", $"msbuild TestApp.csproj --nologo -v:minimal /t:{target}")
|
||||
{
|
||||
WorkingDirectory = workingDirectory,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
};
|
||||
|
||||
using var process = Process.Start(psi)!;
|
||||
var stdOut = await process.StandardOutput.ReadToEndAsync();
|
||||
var stdErr = await process.StandardError.ReadToEndAsync();
|
||||
await process.WaitForExitAsync();
|
||||
|
||||
return (process.ExitCode, stdOut + stdErr);
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.5.2" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
<PackageReference Include="ElectronNET.Core.AspNet" Version="0.5.2" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.6.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
<PackageReference Include="ElectronNET.Core.AspNet" Version="0.6.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="5.9.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -204,29 +204,43 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
|
||||
"integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@humanfs/types": "^0.15.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/node": {
|
||||
"version": "0.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
|
||||
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
|
||||
"integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@humanfs/core": "^0.19.1",
|
||||
"@humanfs/core": "^0.19.2",
|
||||
"@humanfs/types": "^0.15.0",
|
||||
"@humanwhocodes/retry": "^0.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/types": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
|
||||
"integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/module-importer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
|
||||
@@ -526,9 +540,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"version": "1.1.18",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
|
||||
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
@@ -1337,9 +1351,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
|
||||
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
|
||||
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -1797,9 +1811,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/readdir-glob/node_modules/brace-expansion": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
|
||||
"integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
|
||||
@@ -75,8 +75,8 @@
|
||||
<ProjectReference Include="..\ElectronNET.AspNet\ElectronNET.AspNet.csproj" Condition="$(ElectronNetDevMode)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.5.2" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
<PackageReference Include="ElectronNET.Core.AspNet" Version="0.5.2" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.6.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
<PackageReference Include="ElectronNET.Core.AspNet" Version="0.6.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="5.9.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<Import Project="..\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
|
||||
<TargetFrameworks>$(ElectronNetTargetFrameworks)</TargetFrameworks>
|
||||
<PackageOutputPath>..\..\artifacts</PackageOutputPath>
|
||||
<PackageId>$(PackageNamePrefix)</PackageId>
|
||||
<Title>$(PackageId)</Title>
|
||||
|
||||
Binary file not shown.
@@ -4,19 +4,21 @@
|
||||
<PropertyGroup Label="ElectronNetCommon">
|
||||
<ElectronVersion>30.4.0</ElectronVersion>
|
||||
<ElectronBuilderVersion>26.0</ElectronBuilderVersion>
|
||||
<!-- Allow ElectronRuntimeIdentifier to be overridden (explicitly or via RuntimeIdentifier) -->
|
||||
<ElectronRuntimeIdentifier Condition="'$(ElectronRuntimeIdentifier)' == '' AND '$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier)</ElectronRuntimeIdentifier>
|
||||
<!-- When using the dotnet CLI (Core MSBuild), infer the current RID as a default -->
|
||||
<ElectronRuntimeIdentifier Condition="'$(ElectronRuntimeIdentifier)' == '' AND '$(MSBuildRuntimeType)' == 'Core'">$([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier)</ElectronRuntimeIdentifier>
|
||||
<!-- Otherwise, Full MSBuild runs only on Windows, so infer a Windows RID from OSArchitecture -->
|
||||
<ElectronRuntimeIdentifier Condition="'$(ElectronRuntimeIdentifier)' == '' AND '$(MSBuildRuntimeType)' == 'Full'">win-$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLower())</ElectronRuntimeIdentifier>
|
||||
<!-- ElectronRuntimeIdentifier may be set explicitly here; it is otherwise resolved from
|
||||
RuntimeIdentifier (or the host) in ElectronResolveRuntimeIdentifier, which runs late
|
||||
enough to also see RIDs coming from the project file or a publish profile. -->
|
||||
<ElectronSingleInstance>true</ElectronSingleInstance>
|
||||
<ElectronSplashScreen></ElectronSplashScreen>
|
||||
<ElectronIcon></ElectronIcon>
|
||||
<PackageIcon></PackageIcon>
|
||||
<ElectronPackageId>$(MSBuildProjectName.Replace(".", "-").ToLower())</ElectronPackageId>
|
||||
<ElectronPackageId Condition="'$(ElectronPackageId)' == ''">$(MSBuildProjectName.Replace(".", "-").ToLower())</ElectronPackageId>
|
||||
<ElectronBuilderJson>electron-builder.json</ElectronBuilderJson>
|
||||
<Title>$(MSBuildProjectName)</Title>
|
||||
<Title Condition="'$(Title)' == ''">$(MSBuildProjectName)</Title>
|
||||
<!-- Location of the Electron binary relative to the .NET application directory of a packaged app.
|
||||
The default matches the standard layout, where the .NET app is placed in 'resources/bin'. -->
|
||||
<ElectronRootDir Condition="'$(ElectronRootDir)' == ''">../..</ElectronRootDir>
|
||||
<!-- Set to true to suppress the npm/npx invocations of the Electron build and publish targets. -->
|
||||
<ElectronSkipExecCommands Condition="'$(ElectronSkipExecCommands)' == ''">false</ElectronSkipExecCommands>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<PropertyGroup Condition="'$(ElectronExecutable)' == ''">
|
||||
<PropertyGroup Condition="'$(ElectronExecutableName)' == ''">
|
||||
<WinPrefix>win</WinPrefix>
|
||||
<ElectronExecutable Condition="'$(RuntimeIdentifier.StartsWith($(WinPrefix)))' == 'true'">$(Title)</ElectronExecutable>
|
||||
<ElectronExecutable Condition="'$(RuntimeIdentifier.StartsWith($(WinPrefix)))' != 'true'">$(ElectronPackageId)</ElectronExecutable>
|
||||
<ElectronExecutableName Condition="'$(RuntimeIdentifier.StartsWith($(WinPrefix)))' == 'true'">$(Title)</ElectronExecutableName>
|
||||
<ElectronExecutableName Condition="'$(RuntimeIdentifier.StartsWith($(WinPrefix)))' != 'true'">$(ElectronPackageId)</ElectronExecutableName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- The binary Electron.NET launches. It only differs from the packaged executable in advanced
|
||||
scenarios, e.g. when a build hook adds a launcher stub to the package. -->
|
||||
<ElectronExecutable Condition="'$(ElectronExecutable)' == ''">$(ElectronExecutableName)</ElectronExecutable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -19,6 +25,7 @@
|
||||
<ItemGroup>
|
||||
<AssemblyMetadata Include="ElectronExecutable" Value="$(ElectronExecutable)" />
|
||||
<AssemblyMetadata Include="ElectronVersion" Value="$(ElectronVersion)" />
|
||||
<AssemblyMetadata Include="ElectronRootDir" Value="$(ElectronRootDir)" />
|
||||
<AssemblyMetadata Include="RuntimeIdentifier" Value="$(RuntimeIdentifier)" />
|
||||
<AssemblyMetadata Include="ElectronSingleInstance" Value="$(ElectronSingleInstance)" />
|
||||
<AssemblyMetadata Include="Title" Value="$(Title)" />
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
<!-- Targets -->
|
||||
|
||||
<Target Name="ElectronBeforeClean" AfterTargets="BeforeClean" DependsOnTargets="ElectronResolvePaths">
|
||||
<Target Name="ElectronBeforeClean" AfterTargets="BeforeClean" DependsOnTargets="ElectronSetPaths">
|
||||
<ItemGroup>
|
||||
<Clean Include="$(ElectronOutDir)**" />
|
||||
</ItemGroup>
|
||||
@@ -60,7 +60,7 @@
|
||||
<!--<Message Text="ElectronBeforeClean - Clean Files: @(Clean)" Importance="High" />-->
|
||||
</Target>
|
||||
|
||||
<Target Name="ElectronClean" AfterTargets="CoreClean" DependsOnTargets="ElectronResolvePaths">
|
||||
<Target Name="ElectronClean" AfterTargets="CoreClean" DependsOnTargets="ElectronSetPaths">
|
||||
|
||||
<RemoveDir Directories="$(ElectronOutDir)" />
|
||||
|
||||
@@ -95,8 +95,24 @@
|
||||
</Target>
|
||||
|
||||
|
||||
<!-- Resolved at execution time, so that a RuntimeIdentifier coming from the project file,
|
||||
a publish profile or the CLI wins over the host RID. -->
|
||||
<Target Name="ElectronResolveRuntimeIdentifier">
|
||||
|
||||
<PropertyGroup>
|
||||
<ElectronRuntimeIdentifier Condition="'$(ElectronRuntimeIdentifier)' == '' AND '$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier)</ElectronRuntimeIdentifier>
|
||||
<!-- When using the dotnet CLI (Core MSBuild), infer the current RID as a default -->
|
||||
<ElectronRuntimeIdentifier Condition="'$(ElectronRuntimeIdentifier)' == '' AND '$(MSBuildRuntimeType)' == 'Core'">$([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier)</ElectronRuntimeIdentifier>
|
||||
<!-- Otherwise, Full MSBuild runs only on Windows, so infer a Windows RID from OSArchitecture -->
|
||||
<ElectronRuntimeIdentifier Condition="'$(ElectronRuntimeIdentifier)' == '' AND '$(MSBuildRuntimeType)' == 'Full'">win-$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLower())</ElectronRuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<Message Text="ElectronRuntimeIdentifier: $(ElectronRuntimeIdentifier)" Importance="High" />
|
||||
|
||||
</Target>
|
||||
|
||||
<!-- This is also run at design time, each time when a property of the project is changed -->
|
||||
<Target Name="ElectronCreatePackageJson" DependsOnTargets="ResolveProjectReferences;ElectronSetPaths">
|
||||
<Target Name="ElectronCreatePackageJson" DependsOnTargets="ResolveProjectReferences;ElectronSetPaths;ElectronResolveRuntimeIdentifier">
|
||||
|
||||
<Message Text="Creating package.json from template..." Importance="High" />
|
||||
<Message Text="OutDir: $(OutDir)" Importance="High" />
|
||||
@@ -154,7 +170,9 @@
|
||||
<PropertyGroup Condition="'$(UsingMicrosoftNETSdkWeb)' != 'true'">
|
||||
<_NonIntermediatePublishDir>$(PublishDir)</_NonIntermediatePublishDir>
|
||||
<PublishUrl>$(PublishDir)</PublishUrl>
|
||||
<PublishDir>$(IntermediateOutputPath)PubTmp\</PublishDir>
|
||||
<!-- ElectronIntermediatePublishDir allows overriding the intermediate publish directory -->
|
||||
<PublishDir Condition="'$(ElectronIntermediatePublishDir)' != ''">$(ElectronIntermediatePublishDir)</PublishDir>
|
||||
<PublishDir Condition="'$(ElectronIntermediatePublishDir)' == ''">$(IntermediateOutputPath)PubTmp\</PublishDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<RemoveDir Directories="$(PublishDir);$(PublishUrl)" Condition="'$(UsingMicrosoftNETSdkWeb)' != 'true'" />
|
||||
@@ -370,7 +388,7 @@
|
||||
<RemoveDir Directories="$(ElectronHookTargetModuleDir)" Condition="Exists($(ElectronHookTargetModuleDir))" />
|
||||
</Target>
|
||||
|
||||
<Target Name="ElectronCheckVersionMismatch">
|
||||
<Target Name="ElectronCheckVersionMismatch" DependsOnTargets="ElectronResolveRuntimeIdentifier">
|
||||
|
||||
<PropertyGroup>
|
||||
<ElectronArch Condition="'$(ElectronRuntimeIdentifier)' == 'win-x64'">x64</ElectronArch>
|
||||
@@ -443,13 +461,13 @@
|
||||
|
||||
<!--<Message Importance="High" Text="ElectronConfigureApp: CopiedFiles - @(_CopiedFiles)" />-->
|
||||
|
||||
<Message Importance="High" Text="Running command: $(_NpmCmd) in folder: $(ElectronOutputPath)" Condition="@(_CopiedFiles->Count()) > 0" />
|
||||
<Message Importance="High" Text="Running command: $(_NpmCmd) in folder: $(ElectronOutputPath)" Condition="'$(ElectronSkipExecCommands)' != 'true' AND @(_CopiedFiles->Count()) > 0" />
|
||||
|
||||
<Exec Command="$(_NpmCmd)" WorkingDirectory="$(ElectronOutputPath)" Timeout="600000" StandardOutputImportance="High" StandardErrorImportance="High" ContinueOnError="false" Condition="@(_CopiedFiles->Count()) > 0">
|
||||
<Exec Command="$(_NpmCmd)" WorkingDirectory="$(ElectronOutputPath)" Timeout="600000" StandardOutputImportance="High" StandardErrorImportance="High" ContinueOnError="false" Condition="'$(ElectronSkipExecCommands)' != 'true' AND @(_CopiedFiles->Count()) > 0">
|
||||
<Output TaskParameter="ExitCode" PropertyName="ExecExitCode"/>
|
||||
</Exec>
|
||||
|
||||
<Message Importance="High" Text="Electron setup failed!" Condition="'$(ExecExitCode)' != '0'" />
|
||||
<Message Importance="High" Text="Electron setup failed!" Condition="'$(ExecExitCode)' != '' AND '$(ExecExitCode)' != '0'" />
|
||||
|
||||
<PropertyGroup>
|
||||
<_NpmCmd>node node_modules/electron/install.js</_NpmCmd>
|
||||
@@ -458,13 +476,13 @@
|
||||
<_NpmCmd Condition="'$(IsLinuxWsl)' == 'true'">wsl bash -ic '$(_NpmCmd)'</_NpmCmd>
|
||||
</PropertyGroup>
|
||||
|
||||
<Message Importance="High" Text="Running command: $(_NpmCmd) in folder: $(ElectronOutputPath)" Condition="$(ElectronMajor) >= 42 AND @(_CopiedFiles->Count()) > 0" />
|
||||
<Message Importance="High" Text="Running command: $(_NpmCmd) in folder: $(ElectronOutputPath)" Condition="'$(ElectronSkipExecCommands)' != 'true' AND $(ElectronMajor) >= 42 AND @(_CopiedFiles->Count()) > 0" />
|
||||
|
||||
<Exec Command="$(_NpmCmd)" WorkingDirectory="$(ElectronOutputPath)" Timeout="600000" StandardOutputImportance="High" StandardErrorImportance="High" ContinueOnError="false" Condition="$(ElectronMajor) >= 42 AND @(_CopiedFiles->Count()) > 0">
|
||||
<Exec Command="$(_NpmCmd)" WorkingDirectory="$(ElectronOutputPath)" Timeout="600000" StandardOutputImportance="High" StandardErrorImportance="High" ContinueOnError="false" Condition="'$(ElectronSkipExecCommands)' != 'true' AND $(ElectronMajor) >= 42 AND @(_CopiedFiles->Count()) > 0">
|
||||
<Output TaskParameter="ExitCode" PropertyName="ExecExitCode"/>
|
||||
</Exec>
|
||||
|
||||
<Message Importance="High" Text="Electron installation failed!" Condition="'$(ExecExitCode)' != '0'" />
|
||||
<Message Importance="High" Text="Electron installation failed!" Condition="'$(ExecExitCode)' != '' AND '$(ExecExitCode)' != '0'" />
|
||||
|
||||
<!-- Fix up incorrect symlinks created by npm on Windows when targeting macOS -->
|
||||
<PropertyGroup>
|
||||
@@ -599,11 +617,11 @@ For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migr
|
||||
<_NpmAppDepsCmd Condition="'$(IsLinuxWsl)' == 'true'">wsl bash -ic '$(_NpmAppDepsCmd)'</_NpmAppDepsCmd>
|
||||
</PropertyGroup>
|
||||
|
||||
<Exec Command="$(_NpmAppDepsCmd)" WorkingDirectory="$(ElectronAppDirFullPath)" Timeout="600000" StandardOutputImportance="High" StandardErrorImportance="High" ContinueOnError="false" Condition="@(CopiedFiles->Count()) > 0">
|
||||
<Exec Command="$(_NpmAppDepsCmd)" WorkingDirectory="$(ElectronAppDirFullPath)" Timeout="600000" StandardOutputImportance="High" StandardErrorImportance="High" ContinueOnError="false" Condition="'$(ElectronSkipExecCommands)' != 'true' AND @(CopiedFiles->Count()) > 0">
|
||||
<Output TaskParameter="ExitCode" PropertyName="ExecExitCode"/>
|
||||
</Exec>
|
||||
|
||||
<Message Importance="High" Text="Electron app dependency install failed!" Condition="'$(ExecExitCode)' != '0'" />
|
||||
<Message Importance="High" Text="Electron app dependency install failed!" Condition="'$(ExecExitCode)' != '' AND '$(ExecExitCode)' != '0'" />
|
||||
|
||||
<!-- Install electron-builder (the dev tool) into the publish root, which is the
|
||||
electron-builder projectDir (cwd). app metadata + runtime deps are read from
|
||||
@@ -615,6 +633,7 @@ For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migr
|
||||
app\package.json instead. -->
|
||||
<ItemGroup>
|
||||
<DevTemplateProperty Include="ElectronPackageId" Value="$(ElectronPackageId)" />
|
||||
<DevTemplateProperty Include="ElectronExecutableName" Value="$(ElectronExecutableName)" />
|
||||
<DevTemplateProperty Include="Title" Value="$(Title)" />
|
||||
<DevTemplateProperty Include="Version" Value="$(Version)" />
|
||||
</ItemGroup>
|
||||
@@ -629,11 +648,11 @@ For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migr
|
||||
<_NpmCmd Condition="'$(IsLinuxWsl)' == 'true'">wsl bash -ic '$(_NpmCmd)'</_NpmCmd>
|
||||
</PropertyGroup>
|
||||
|
||||
<Exec Command="$(_NpmCmd)" WorkingDirectory="$(ElectronPublishDirFullPath)" Timeout="600000" StandardOutputImportance="High" StandardErrorImportance="High" ContinueOnError="false" Condition="@(CopiedFiles->Count()) > 0">
|
||||
<Exec Command="$(_NpmCmd)" WorkingDirectory="$(ElectronPublishDirFullPath)" Timeout="600000" StandardOutputImportance="High" StandardErrorImportance="High" ContinueOnError="false" Condition="'$(ElectronSkipExecCommands)' != 'true' AND @(CopiedFiles->Count()) > 0">
|
||||
<Output TaskParameter="ExitCode" PropertyName="ExecExitCode"/>
|
||||
</Exec>
|
||||
|
||||
<Message Importance="High" Text="Electron setup failed!" Condition="'$(ExecExitCode)' != '0'" />
|
||||
<Message Importance="High" Text="Electron setup failed!" Condition="'$(ExecExitCode)' != '' AND '$(ExecExitCode)' != '0'" />
|
||||
|
||||
<!-- Transform ElectronPublishUrlFullPath to a wslpath if IsLinuxWsl is true -->
|
||||
<Exec Command="wsl wslpath '$(ElectronPublishUrlFullPath)'" ConsoleToMsBuild="true" Condition="'$(IsLinuxWsl)' == 'true'">
|
||||
@@ -653,18 +672,15 @@ For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migr
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<_NpxCmd>npx electron-builder --config=./$(ElectronBuilderJson)</_NpxCmd>
|
||||
<_NpxCmd Condition="'$(IsLinuxWsl)' == 'true'">$(_NpxCmd) --$(ElectronPlatform)</_NpxCmd>
|
||||
<_NpxCmd Condition="'$(IsLinuxWsl)' == 'true'">$(_NpxCmd) --$(ElectronArch)</_NpxCmd>
|
||||
<_NpxCmd>$(_NpxCmd) -c.electronVersion=$(ElectronVersion) -c.directories.output "$(ElectronPublishUrlFullPath)" $(ElectronPaParams)</_NpxCmd>
|
||||
<_NpxCmd>npx electron-builder -c.$(ElectronPlatform).defaultArch=$(ElectronArch) --config=./$(ElectronBuilderJson) -c.electronVersion=$(ElectronVersion) --$(ElectronPlatform) --$(ElectronArch) -c.directories.output "$(ElectronPublishUrlFullPath)" $(ElectronPaParams)</_NpxCmd>
|
||||
<_NpxCmd Condition="'$(IsLinuxWsl)' == 'true'">wsl bash -ic '$(_NpxCmd)'</_NpxCmd>
|
||||
</PropertyGroup>
|
||||
|
||||
<Exec Command="$(_NpxCmd)" WorkingDirectory="$(ElectronPublishDirFullPath)" Timeout="1800000" StandardOutputImportance="High" StandardErrorImportance="High" ContinueOnError="false" Condition="@(CopiedFiles->Count()) > 0">
|
||||
<Exec Command="$(_NpxCmd)" WorkingDirectory="$(ElectronPublishDirFullPath)" Timeout="1800000" StandardOutputImportance="High" StandardErrorImportance="High" ContinueOnError="false" Condition="'$(ElectronSkipExecCommands)' != 'true' AND @(CopiedFiles->Count()) > 0">
|
||||
<Output TaskParameter="ExitCode" PropertyName="ExecExitCode"/>
|
||||
</Exec>
|
||||
|
||||
<Message Importance="High" Text="Electron setup failed!" Condition="'$(ExecExitCode)' != '0'" />
|
||||
<Message Importance="High" Text="Electron setup failed!" Condition="'$(ExecExitCode)' != '' AND '$(ExecExitCode)' != '0'" />
|
||||
|
||||
</Target>
|
||||
|
||||
|
||||
@@ -553,6 +553,30 @@
|
||||
Category="General">
|
||||
</StringProperty>
|
||||
|
||||
<StringProperty Name="ElectronExecutableName"
|
||||
DisplayName="Executable Name"
|
||||
Description="Name of the executable inside the package, without extension. Defaults to the title on Windows and to the package id on Linux and macOS"
|
||||
Category="General">
|
||||
</StringProperty>
|
||||
|
||||
<StringProperty Name="ElectronRootDir"
|
||||
DisplayName="Electron Root Directory"
|
||||
Description="Location of the Electron binary relative to the .NET application directory of a packaged app. Default: ../.. (the .NET app lives in resources/bin)"
|
||||
Category="General">
|
||||
</StringProperty>
|
||||
|
||||
<BoolProperty Name="ElectronSkipExecCommands"
|
||||
DisplayName="Skip npm/npx Commands"
|
||||
Description="If enabled, the npm and npx invocations of the Electron build and publish targets are skipped"
|
||||
Category="General">
|
||||
</BoolProperty>
|
||||
|
||||
<StringProperty Name="ElectronIntermediatePublishDir"
|
||||
DisplayName="Intermediate Publish Directory"
|
||||
Description="Overrides the intermediate publish directory used while packaging. Default: $(IntermediateOutputPath)PubTmp\"
|
||||
Category="General">
|
||||
</StringProperty>
|
||||
|
||||
<StringProperty Name="ElectronSplashScreen"
|
||||
DisplayName="Splash Screen Image"
|
||||
Description="Choose a PNG image to be shown as splash screen on startup"
|
||||
|
||||
@@ -4,11 +4,17 @@
|
||||
"private": true,
|
||||
"build": {
|
||||
"appId": "$(ElectronPackageId)",
|
||||
"win": {
|
||||
"executableName": "$(ElectronExecutableName)"
|
||||
},
|
||||
"mac": {
|
||||
"executableName": "$(ElectronExecutableName)"
|
||||
},
|
||||
"linux": {
|
||||
"desktop": {
|
||||
"entry": { "Name": "$(Title)" }
|
||||
},
|
||||
"executableName": "$(ElectronPackageId)"
|
||||
"executableName": "$(ElectronExecutableName)"
|
||||
},
|
||||
"deb": {
|
||||
"desktop": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>0.5.2</Version>
|
||||
<Version>0.6.0</Version>
|
||||
<PackageNamePrefix>ElectronNET.Core</PackageNamePrefix>
|
||||
<Authors>Gregor Biswanger, Florian Rappl, softworkz</Authors>
|
||||
<Product>Electron.NET</Product>
|
||||
@@ -18,6 +18,7 @@
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<Version>$(Version)$(VersionPostFix)</Version>
|
||||
<InformationalVersion>$(Version)</InformationalVersion>
|
||||
<ElectronNetTargetFrameworks Condition="'$(ElectronNetTargetFrameworks)' == ''">net6.0;net8.0;net10.0</ElectronNetTargetFrameworks>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user