Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bundler): add --windows-icon, --windows-no-console, fix bun.exe's main icon #15894

Merged
merged 9 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions cmake/targets/BuildBun.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,8 @@ file(GLOB BUN_C_SOURCES ${CONFIGURE_DEPENDS}

if(WIN32)
list(APPEND BUN_C_SOURCES ${CWD}/src/bun.js/bindings/windows/musl-memmem.c)
list(APPEND BUN_CXX_SOURCES ${CWD}/src/bun.js/bindings/windows/rescle.cpp)
list(APPEND BUN_CXX_SOURCES ${CWD}/src/bun.js/bindings/windows/rescle-binding.cpp)
endif()

register_repository(
Expand Down Expand Up @@ -650,19 +652,27 @@ if(WIN32)
set(Bun_VERSION_WITH_TAG ${VERSION})
endif()
set(BUN_ICO_PATH ${CWD}/src/bun.ico)
configure_file(${CWD}/src/bun.ico ${CODEGEN_PATH}/bun.ico COPYONLY)
configure_file(
${CWD}/src/windows-app-info.rc
${CODEGEN_PATH}/windows-app-info.rc
@ONLY
)
list(APPEND BUN_CPP_SOURCES ${CODEGEN_PATH}/windows-app-info.rc)
add_custom_command(
OUTPUT ${CODEGEN_PATH}/windows-app-info.res
COMMAND rc.exe /fo ${CODEGEN_PATH}/windows-app-info.res ${CODEGEN_PATH}/windows-app-info.rc
DEPENDS ${CODEGEN_PATH}/windows-app-info.rc ${CODEGEN_PATH}/bun.ico
COMMENT "Adding Windows resource file ${CODEGEN_PATH}/windows-app-info.res with ico in ${CODEGEN_PATH}/bun.ico"
)
set(WINDOWS_RESOURCES ${CODEGEN_PATH}/windows-app-info.res)
endif()

# --- Executable ---

set(BUN_CPP_OUTPUT ${BUILD_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}${bun}${CMAKE_STATIC_LIBRARY_SUFFIX})

if(BUN_LINK_ONLY)
add_executable(${bun} ${BUN_CPP_OUTPUT} ${BUN_ZIG_OUTPUT})
add_executable(${bun} ${BUN_CPP_OUTPUT} ${BUN_ZIG_OUTPUT} ${WINDOWS_RESOURCES})
set_target_properties(${bun} PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(${bun} PRIVATE ${BUN_CPP_OUTPUT})
elseif(BUN_CPP_ONLY)
Expand All @@ -680,7 +690,7 @@ elseif(BUN_CPP_ONLY)
${BUN_CPP_OUTPUT}
)
else()
add_executable(${bun} ${BUN_CPP_SOURCES})
add_executable(${bun} ${BUN_CPP_SOURCES} ${WINDOWS_RESOURCES})
target_link_libraries(${bun} PRIVATE ${BUN_ZIG_OUTPUT})
endif()

Expand Down
13 changes: 13 additions & 0 deletions docs/bundler/executables.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,19 @@ $ bun build --compile --asset-naming="[name].[ext]" ./index.ts

To trim down the size of the executable a little, pass `--minify` to `bun build --compile`. This uses Bun's minifier to reduce the code size. Overall though, Bun's binary is still way too big and we need to make it smaller.

## Windows-specific flags

When compiling a standalone executable on Windows, there are two platform-specific options that can be used to customize metadata on the generated `.exe` file:

- `--windows-icon=path/to/icon.ico` to customize the executable file icon.
- `--windows-hide-console` to disable the background terminal, which can be used for applications that do not need a TTY.

{% callout %}

These flags currently cannot be used when cross-compiling because they depend on Windows APIs.

{% /callout %}

## Unsupported CLI arguments

Currently, the `--compile` flag can only accept a single entrypoint at a time and does not support the following flags:
Expand Down
29 changes: 27 additions & 2 deletions src/StandaloneModuleGraph.zig
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,11 @@ pub const StandaloneModuleGraph = struct {
else
std.mem.page_size;

pub fn inject(bytes: []const u8, self_exe: [:0]const u8) bun.FileDescriptor {
pub const InjectOptions = struct {
windows_hide_console: bool = false,
};

pub fn inject(bytes: []const u8, self_exe: [:0]const u8, inject_options: InjectOptions) bun.FileDescriptor {
var buf: bun.PathBuffer = undefined;
var zname: [:0]const u8 = bun.span(bun.fs.FileSystem.instance.tmpname("bun-build", &buf, @as(u64, @bitCast(std.time.milliTimestamp()))) catch |err| {
Output.prettyErrorln("<r><red>error<r><d>:<r> failed to get temporary file name: {s}", .{@errorName(err)});
Expand Down Expand Up @@ -470,7 +474,7 @@ pub const StandaloneModuleGraph = struct {
bun.invalid_fd,
out,
// access_mask
w.SYNCHRONIZE | w.GENERIC_WRITE | w.DELETE,
w.SYNCHRONIZE | w.GENERIC_WRITE | w.GENERIC_READ | w.DELETE,
// create disposition
w.FILE_OPEN,
// create options
Expand Down Expand Up @@ -637,6 +641,15 @@ pub const StandaloneModuleGraph = struct {
_ = bun.C.fchmod(cloned_executable_fd.int(), 0o777);
}

if (Environment.isWindows and inject_options.windows_hide_console) {
bun.windows.editWin32BinarySubsystem(.{ .handle = cloned_executable_fd }, .windows_gui) catch |err| {
Output.err(err, "failed to disable console on executable", .{});
cleanup(zname, cloned_executable_fd);

Global.exit(1);
};
}

return cloned_executable_fd;
}

Expand Down Expand Up @@ -664,6 +677,8 @@ pub const StandaloneModuleGraph = struct {
outfile: []const u8,
env: *bun.DotEnv.Loader,
output_format: bun.options.Format,
windows_hide_console: bool,
windows_icon: ?[]const u8,
) !void {
const bytes = try toBytes(allocator, module_prefix, output_files, output_format);
if (bytes.len == 0) return;
Expand All @@ -680,6 +695,7 @@ pub const StandaloneModuleGraph = struct {
Output.err(err, "failed to download cross-compiled bun executable", .{});
Global.exit(1);
},
.{ .windows_hide_console = windows_hide_console },
);
fd.assertKind(.system);

Expand All @@ -704,6 +720,15 @@ pub const StandaloneModuleGraph = struct {

Global.exit(1);
};
_ = bun.sys.close(fd);

if (windows_icon) |icon_utf8| {
var icon_buf: bun.OSPathBuffer = undefined;
const icon = bun.strings.toWPathNormalized(&icon_buf, icon_utf8);
bun.windows.rescle.setIcon(outfile_slice, icon) catch {
Output.warn("Failed to set executable icon", .{});
};
}
return;
}

Expand Down
14 changes: 14 additions & 0 deletions src/bun.js/bindings/windows/rescle-binding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include "root.h"
#include "rescle.h"

extern "C" int rescle__setIcon(const WCHAR* exeFilename, const WCHAR* iconFilename)
{
rescle::ResourceUpdater updater;
if (!updater.Load(exeFilename))
return -1;
if (!updater.SetIcon(iconFilename))
return -2;
if (!updater.Commit())
return -3;
return 0;
}
Loading
Loading