authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-21 11:59:24-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-11-21 11:59:24-05:00
logbe6f76655fb355426dc8f3824368376608bc0ba1
tree87a1557f767a12f8577feda20bf603037c166429
parent27b34a5b77623b106ae1e1c7c4a466608a10905e
parent9f363cd679a60be6d9a4378f9a50c050fde9708d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #18055 from ziglang/zig-init


11 files changed, 264 insertions(+), 240 deletions(-)

lib/init-exe/build.zig deleted-70
......@@ -1,70 +0,0 @@
1const std = @import("std");
2
3// Although this function looks imperative, note that its job is to
4// declaratively construct a build graph that will be executed by an external
5// runner.
6pub fn build(b: *std.Build) void {
7 // Standard target options allows the person running `zig build` to choose
8 // what target to build for. Here we do not override the defaults, which
9 // means any target is allowed, and the default is native. Other options
10 // for restricting supported target set are available.
11 const target = b.standardTargetOptions(.{});
12
13 // Standard optimization options allow the person running `zig build` to select
14 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
15 // set a preferred release mode, allowing the user to decide how to optimize.
16 const optimize = b.standardOptimizeOption(.{});
17
18 const exe = b.addExecutable(.{
19 .name = "$",
20 // In this case the main source file is merely a path, however, in more
21 // complicated build scripts, this could be a generated file.
22 .root_source_file = .{ .path = "src/main.zig" },
23 .target = target,
24 .optimize = optimize,
25 });
26
27 // This declares intent for the executable to be installed into the
28 // standard location when the user invokes the "install" step (the default
29 // step when running `zig build`).
30 b.installArtifact(exe);
31
32 // This *creates* a Run step in the build graph, to be executed when another
33 // step is evaluated that depends on it. The next line below will establish
34 // such a dependency.
35 const run_cmd = b.addRunArtifact(exe);
36
37 // By making the run step depend on the install step, it will be run from the
38 // installation directory rather than directly from within the cache directory.
39 // This is not necessary, however, if the application depends on other installed
40 // files, this ensures they will be present and in the expected location.
41 run_cmd.step.dependOn(b.getInstallStep());
42
43 // This allows the user to pass arguments to the application in the build
44 // command itself, like this: `zig build run -- arg1 arg2 etc`
45 if (b.args) |args| {
46 run_cmd.addArgs(args);
47 }
48
49 // This creates a build step. It will be visible in the `zig build --help` menu,
50 // and can be selected like this: `zig build run`
51 // This will evaluate the `run` step rather than the default, which is "install".
52 const run_step = b.step("run", "Run the app");
53 run_step.dependOn(&run_cmd.step);
54
55 // Creates a step for unit testing. This only builds the test executable
56 // but does not run it.
57 const unit_tests = b.addTest(.{
58 .root_source_file = .{ .path = "src/main.zig" },
59 .target = target,
60 .optimize = optimize,
61 });
62
63 const run_unit_tests = b.addRunArtifact(unit_tests);
64
65 // Similar to creating the run step earlier, this exposes a `test` step to
66 // the `zig build --help` menu, providing a way for the user to request
67 // running the unit tests.
68 const test_step = b.step("test", "Run unit tests");
69 test_step.dependOn(&run_unit_tests.step);
70}
lib/init-exe/src/main.zig deleted-24
......@@ -1,24 +0,0 @@
1const std = @import("std");
2
3pub fn main() !void {
4 // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`)
5 std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
6
7 // stdout is for the actual output of your application, for example if you
8 // are implementing gzip, then only the compressed bytes should be sent to
9 // stdout, not any debugging messages.
10 const stdout_file = std.io.getStdOut().writer();
11 var bw = std.io.bufferedWriter(stdout_file);
12 const stdout = bw.writer();
13
14 try stdout.print("Run `zig build test` to run the tests.\n", .{});
15
16 try bw.flush(); // don't forget to flush!
17}
18
19test "simple test" {
20 var list = std.ArrayList(i32).init(std.testing.allocator);
21 defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
22 try list.append(42);
23 try std.testing.expectEqual(@as(i32, 42), list.pop());
24}
lib/init-lib/build.zig deleted-47
......@@ -1,47 +0,0 @@
1const std = @import("std");
2
3// Although this function looks imperative, note that its job is to
4// declaratively construct a build graph that will be executed by an external
5// runner.
6pub fn build(b: *std.Build) void {
7 // Standard target options allows the person running `zig build` to choose
8 // what target to build for. Here we do not override the defaults, which
9 // means any target is allowed, and the default is native. Other options
10 // for restricting supported target set are available.
11 const target = b.standardTargetOptions(.{});
12
13 // Standard optimization options allow the person running `zig build` to select
14 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
15 // set a preferred release mode, allowing the user to decide how to optimize.
16 const optimize = b.standardOptimizeOption(.{});
17
18 const lib = b.addStaticLibrary(.{
19 .name = "$",
20 // In this case the main source file is merely a path, however, in more
21 // complicated build scripts, this could be a generated file.
22 .root_source_file = .{ .path = "src/main.zig" },
23 .target = target,
24 .optimize = optimize,
25 });
26
27 // This declares intent for the library to be installed into the standard
28 // location when the user invokes the "install" step (the default step when
29 // running `zig build`).
30 b.installArtifact(lib);
31
32 // Creates a step for unit testing. This only builds the test executable
33 // but does not run it.
34 const main_tests = b.addTest(.{
35 .root_source_file = .{ .path = "src/main.zig" },
36 .target = target,
37 .optimize = optimize,
38 });
39
40 const run_main_tests = b.addRunArtifact(main_tests);
41
42 // This creates a build step. It will be visible in the `zig build --help` menu,
43 // and can be selected like this: `zig build test`
44 // This will evaluate the `test` step rather than the default, which is "install".
45 const test_step = b.step("test", "Run library tests");
46 test_step.dependOn(&run_main_tests.step);
47}
lib/init-lib/src/main.zig deleted-10
......@@ -1,10 +0,0 @@
1const std = @import("std");
2const testing = std.testing;
3
4export fn add(a: i32, b: i32) i32 {
5 return a + b;
6}
7
8test "basic add functionality" {
9 try testing.expect(add(3, 7) == 10);
10}
lib/init/build.zig created+91
......@@ -0,0 +1,91 @@
1const std = @import("std");
2
3// Although this function looks imperative, note that its job is to
4// declaratively construct a build graph that will be executed by an external
5// runner.
6pub fn build(b: *std.Build) void {
7 // Standard target options allows the person running `zig build` to choose
8 // what target to build for. Here we do not override the defaults, which
9 // means any target is allowed, and the default is native. Other options
10 // for restricting supported target set are available.
11 const target = b.standardTargetOptions(.{});
12
13 // Standard optimization options allow the person running `zig build` to select
14 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
15 // set a preferred release mode, allowing the user to decide how to optimize.
16 const optimize = b.standardOptimizeOption(.{});
17
18 const lib = b.addStaticLibrary(.{
19 .name = "$",
20 // In this case the main source file is merely a path, however, in more
21 // complicated build scripts, this could be a generated file.
22 .root_source_file = .{ .path = "src/root.zig" },
23 .target = target,
24 .optimize = optimize,
25 });
26
27 // This declares intent for the library to be installed into the standard
28 // location when the user invokes the "install" step (the default step when
29 // running `zig build`).
30 b.installArtifact(lib);
31
32 const exe = b.addExecutable(.{
33 .name = "$",
34 .root_source_file = .{ .path = "src/main.zig" },
35 .target = target,
36 .optimize = optimize,
37 });
38
39 // This declares intent for the executable to be installed into the
40 // standard location when the user invokes the "install" step (the default
41 // step when running `zig build`).
42 b.installArtifact(exe);
43
44 // This *creates* a Run step in the build graph, to be executed when another
45 // step is evaluated that depends on it. The next line below will establish
46 // such a dependency.
47 const run_cmd = b.addRunArtifact(exe);
48
49 // By making the run step depend on the install step, it will be run from the
50 // installation directory rather than directly from within the cache directory.
51 // This is not necessary, however, if the application depends on other installed
52 // files, this ensures they will be present and in the expected location.
53 run_cmd.step.dependOn(b.getInstallStep());
54
55 // This allows the user to pass arguments to the application in the build
56 // command itself, like this: `zig build run -- arg1 arg2 etc`
57 if (b.args) |args| {
58 run_cmd.addArgs(args);
59 }
60
61 // This creates a build step. It will be visible in the `zig build --help` menu,
62 // and can be selected like this: `zig build run`
63 // This will evaluate the `run` step rather than the default, which is "install".
64 const run_step = b.step("run", "Run the app");
65 run_step.dependOn(&run_cmd.step);
66
67 // Creates a step for unit testing. This only builds the test executable
68 // but does not run it.
69 const lib_unit_tests = b.addTest(.{
70 .root_source_file = .{ .path = "src/root.zig" },
71 .target = target,
72 .optimize = optimize,
73 });
74
75 const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
76
77 const exe_unit_tests = b.addTest(.{
78 .root_source_file = .{ .path = "src/main.zig" },
79 .target = target,
80 .optimize = optimize,
81 });
82
83 const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
84
85 // Similar to creating the run step earlier, this exposes a `test` step to
86 // the `zig build --help` menu, providing a way for the user to request
87 // running the unit tests.
88 const test_step = b.step("test", "Run unit tests");
89 test_step.dependOn(&run_lib_unit_tests.step);
90 test_step.dependOn(&run_exe_unit_tests.step);
91}
lib/init/build.zig.zon created+61
......@@ -0,0 +1,61 @@
1.{
2 .name = "$",
3 // This is a [Semantic Version](https://semver.org/).
4 // In a future version of Zig it will be used for package deduplication.
5 .version = "0.0.0",
6
7 // This field is optional.
8 // This is currently advisory only; Zig does not yet do anything
9 // with this value.
10 //.minimum_zig_version = "0.11.0",
11
12 // This field is optional.
13 // Each dependency must either provide a `url` and `hash`, or a `path`.
14 // `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
15 // Once all dependencies are fetched, `zig build` no longer requires
16 // Internet connectivity.
17 .dependencies = .{
18 // A future version of Zig will provide a `zig add <url>` subcommand
19 // for easily adding dependencies.
20 //.example = .{
21 // // When updating this field to a new URL, be sure to delete the corresponding
22 // // `hash`, otherwise you are communicating that you expect to find the old hash at
23 // // the new URL.
24 // .url = "https://example.com/foo.tar.gz",
25 //
26 // // This is computed from the file contents of the directory of files that is
27 // // obtained after fetching `url` and applying the inclusion rules given by
28 // // `paths`.
29 // //
30 // // This field is the source of truth; packages do not come from an `url`; they
31 // // come from a `hash`. `url` is just one of many possible mirrors for how to
32 // // obtain a package matching this `hash`.
33 // //
34 // // Uses the [multihash](https://multiformats.io/multihash/) format.
35 // .hash = "...",
36 //
37 // // When this is provided, the package is found in a directory relative to the
38 // // build root. In this case the package's hash is irrelevant and therefore not
39 // // computed.
40 // .path = "foo",
41 //},
42 },
43
44 // Specifies the set of files and directories that are included in this package.
45 // Only files and directories listed here are included in the `hash` that
46 // is computed for this package.
47 // Paths are relative to the build root. Use the empty string (`""`) to refer to
48 // the build root itself.
49 // A directory listed here means that all files within, recursively, are included.
50 .paths = .{
51 // This makes *all* files, recursively, included in this package. It is generally
52 // better to explicitly list the files and directories instead, to insure that
53 // fetching from tarballs, file system paths, and version control all result
54 // in the same contents hash.
55 "",
56 // For example...
57 //"build.zig",
58 //"build.zig.zon",
59 //"src",
60 },
61}
lib/init/src/main.zig created+24
......@@ -0,0 +1,24 @@
1const std = @import("std");
2
3pub fn main() !void {
4 // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`)
5 std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
6
7 // stdout is for the actual output of your application, for example if you
8 // are implementing gzip, then only the compressed bytes should be sent to
9 // stdout, not any debugging messages.
10 const stdout_file = std.io.getStdOut().writer();
11 var bw = std.io.bufferedWriter(stdout_file);
12 const stdout = bw.writer();
13
14 try stdout.print("Run `zig build test` to run the tests.\n", .{});
15
16 try bw.flush(); // don't forget to flush!
17}
18
19test "simple test" {
20 var list = std.ArrayList(i32).init(std.testing.allocator);
21 defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
22 try list.append(42);
23 try std.testing.expectEqual(@as(i32, 42), list.pop());
24}
lib/init/src/root.zig created+10
......@@ -0,0 +1,10 @@
1const std = @import("std");
2const testing = std.testing;
3
4export fn add(a: i32, b: i32) i32 {
5 return a + b;
6}
7
8test "basic add functionality" {
9 try testing.expect(add(3, 7) == 10);
10}
lib/std/fs.zig+21-5
......@@ -2564,12 +2564,28 @@ pub const Dir = struct {
25642564 };
25652565 }
25662566
2567 /// Writes content to the file system, creating a new file if it does not exist, truncating
2568 /// if it already exists.
2569 pub fn writeFile(self: Dir, sub_path: []const u8, data: []const u8) !void {
2570 var file = try self.createFile(sub_path, .{});
2567 pub const WriteFileError = File.WriteError || File.OpenError;
2568
2569 /// Deprecated: use `writeFile2`.
2570 pub fn writeFile(self: Dir, sub_path: []const u8, data: []const u8) WriteFileError!void {
2571 return writeFile2(self, .{
2572 .sub_path = sub_path,
2573 .data = data,
2574 .flags = .{},
2575 });
2576 }
2577
2578 pub const WriteFileOptions = struct {
2579 sub_path: []const u8,
2580 data: []const u8,
2581 flags: File.CreateFlags = .{},
2582 };
2583
2584 /// Writes content to the file system, using the file creation flags provided.
2585 pub fn writeFile2(self: Dir, options: WriteFileOptions) WriteFileError!void {
2586 var file = try self.createFile(options.sub_path, options.flags);
25712587 defer file.close();
2572 try file.writeAll(data);
2588 try file.writeAll(options.data);
25732589 }
25742590
25752591 pub const AccessError = os.AccessError;
src/main.zig+49-54
......@@ -86,8 +86,7 @@ const normal_usage =
8686 \\
8787 \\ build Build project from build.zig
8888 \\ fetch Copy a package into global cache and print its hash
89 \\ init-exe Initialize a `zig build` application in the cwd
90 \\ init-lib Initialize a `zig build` library in the cwd
89 \\ init Initialize a Zig package in the current directory
9190 \\
9291 \\ ast-check Look for simple compile errors in any set of files
9392 \\ build-exe Create executable from source or object files
......@@ -320,10 +319,8 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
320319 return cmdFetch(gpa, arena, cmd_args);
321320 } else if (mem.eql(u8, cmd, "libc")) {
322321 return cmdLibC(gpa, cmd_args);
323 } else if (mem.eql(u8, cmd, "init-exe")) {
324 return cmdInit(gpa, arena, cmd_args, .Exe);
325 } else if (mem.eql(u8, cmd, "init-lib")) {
326 return cmdInit(gpa, arena, cmd_args, .Lib);
322 } else if (mem.eql(u8, cmd, "init")) {
323 return cmdInit(gpa, arena, cmd_args);
327324 } else if (mem.eql(u8, cmd, "targets")) {
328325 const info = try detectNativeTargetInfo(.{});
329326 const stdout = io.getStdOut().writer();
......@@ -4835,8 +4832,7 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void {
48354832}
48364833
48374834pub const usage_init =
4838 \\Usage: zig init-exe
4839 \\ zig init-lib
4835 \\Usage: zig init
48404836 \\
48414837 \\ Initializes a `zig build` project in the current working
48424838 \\ directory.
......@@ -4847,12 +4843,7 @@ pub const usage_init =
48474843 \\
48484844;
48494845
4850pub fn cmdInit(
4851 gpa: Allocator,
4852 arena: Allocator,
4853 args: []const []const u8,
4854 output_mode: std.builtin.OutputMode,
4855) !void {
4846pub fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
48564847 _ = gpa;
48574848 {
48584849 var i: usize = 0;
......@@ -4877,14 +4868,12 @@ pub fn cmdInit(
48774868 defer zig_lib_directory.handle.close();
48784869
48794870 const s = fs.path.sep_str;
4880 const template_sub_path = switch (output_mode) {
4881 .Obj => unreachable,
4882 .Lib => "init-lib",
4883 .Exe => "init-exe",
4884 };
4871 const template_sub_path = "init";
48854872 var template_dir = zig_lib_directory.handle.openDir(template_sub_path, .{}) catch |err| {
48864873 const path = zig_lib_directory.path orelse ".";
4887 fatal("unable to open zig project template directory '{s}{s}{s}': {s}", .{ path, s, template_sub_path, @errorName(err) });
4874 fatal("unable to open zig project template directory '{s}{s}{s}': {s}", .{
4875 path, s, template_sub_path, @errorName(err),
4876 });
48884877 };
48894878 defer template_dir.close();
48904879
......@@ -4892,46 +4881,52 @@ pub fn cmdInit(
48924881 const cwd_basename = fs.path.basename(cwd_path);
48934882
48944883 const max_bytes = 10 * 1024 * 1024;
4895 const build_zig_contents = template_dir.readFileAlloc(arena, "build.zig", max_bytes) catch |err| {
4896 fatal("unable to read template file 'build.zig': {s}", .{@errorName(err)});
4884 const template_paths = [_][]const u8{
4885 "build.zig",
4886 "build.zig.zon",
4887 "src" ++ s ++ "main.zig",
4888 "src" ++ s ++ "root.zig",
48974889 };
4898 var modified_build_zig_contents = try std.ArrayList(u8).initCapacity(arena, build_zig_contents.len);
4899 for (build_zig_contents) |c| {
4900 if (c == '$') {
4901 try modified_build_zig_contents.appendSlice(cwd_basename);
4902 } else {
4903 try modified_build_zig_contents.append(c);
4890 var ok_count: usize = 0;
4891
4892 for (template_paths) |template_path| {
4893 if (fs.path.dirname(template_path)) |dirname| {
4894 fs.cwd().makePath(dirname) catch |err| {
4895 fatal("unable to make path '{s}': {s}", .{ dirname, @errorName(err) });
4896 };
49044897 }
4905 }
4906 const main_zig_contents = template_dir.readFileAlloc(arena, "src" ++ s ++ "main.zig", max_bytes) catch |err| {
4907 fatal("unable to read template file 'main.zig': {s}", .{@errorName(err)});
4908 };
4909 if (fs.cwd().access("build.zig", .{})) |_| {
4910 fatal("existing build.zig file would be overwritten", .{});
4911 } else |err| switch (err) {
4912 error.FileNotFound => {},
4913 else => fatal("unable to test existence of build.zig: {s}\n", .{@errorName(err)}),
4914 }
4915 if (fs.cwd().access("src" ++ s ++ "main.zig", .{})) |_| {
4916 fatal("existing src" ++ s ++ "main.zig file would be overwritten", .{});
4917 } else |err| switch (err) {
4918 error.FileNotFound => {},
4919 else => fatal("unable to test existence of src" ++ s ++ "main.zig: {s}\n", .{@errorName(err)}),
4920 }
4921 var src_dir = try fs.cwd().makeOpenPath("src", .{});
4922 defer src_dir.close();
49234898
4924 try src_dir.writeFile("main.zig", main_zig_contents);
4925 try fs.cwd().writeFile("build.zig", modified_build_zig_contents.items);
4899 const contents = template_dir.readFileAlloc(arena, template_path, max_bytes) catch |err| {
4900 fatal("unable to read template file '{s}': {s}", .{ template_path, @errorName(err) });
4901 };
4902 var modified_contents = try std.ArrayList(u8).initCapacity(arena, contents.len);
4903 for (contents) |c| {
4904 if (c == '$') {
4905 try modified_contents.appendSlice(cwd_basename);
4906 } else {
4907 try modified_contents.append(c);
4908 }
4909 }
49264910
4927 std.log.info("Created build.zig", .{});
4928 std.log.info("Created src" ++ s ++ "main.zig", .{});
4911 if (fs.cwd().writeFile2(.{
4912 .sub_path = template_path,
4913 .data = modified_contents.items,
4914 .flags = .{ .exclusive = true },
4915 })) |_| {
4916 std.log.info("created {s}", .{template_path});
4917 ok_count += 1;
4918 } else |err| switch (err) {
4919 error.PathAlreadyExists => std.log.info("preserving already existing file: {s}", .{
4920 template_path,
4921 }),
4922 else => std.log.err("unable to write {s}: {s}\n", .{ template_path, @errorName(err) }),
4923 }
4924 }
49294925
4930 switch (output_mode) {
4931 .Lib => std.log.info("Next, try `zig build --help` or `zig build test`", .{}),
4932 .Exe => std.log.info("Next, try `zig build --help` or `zig build run`", .{}),
4933 .Obj => unreachable,
4926 if (ok_count == template_paths.len) {
4927 std.log.info("see `zig build --help` for a menu of options", .{});
49344928 }
4929 return cleanExit();
49354930}
49364931
49374932pub const usage_build =
test/tests.zig+8-30
......@@ -776,39 +776,17 @@ pub fn addCliTests(b: *std.Build) *Step {
776776 const s = std.fs.path.sep_str;
777777
778778 {
779
780 // Test `zig init-lib`.
781 const tmp_path = b.makeTempPath();
782 const init_lib = b.addSystemCommand(&.{ b.zig_exe, "init-lib" });
783 init_lib.setCwd(.{ .cwd_relative = tmp_path });
784 init_lib.setName("zig init-lib");
785 init_lib.expectStdOutEqual("");
786 init_lib.expectStdErrEqual("info: Created build.zig\n" ++
787 "info: Created src" ++ s ++ "main.zig\n" ++
788 "info: Next, try `zig build --help` or `zig build test`\n");
789
790 const run_test = b.addSystemCommand(&.{ b.zig_exe, "build", "test" });
791 run_test.setCwd(.{ .cwd_relative = tmp_path });
792 run_test.setName("zig build test");
793 run_test.expectStdOutEqual("");
794 run_test.step.dependOn(&init_lib.step);
795
796 const cleanup = b.addRemoveDirTree(tmp_path);
797 cleanup.step.dependOn(&run_test.step);
798
799 step.dependOn(&cleanup.step);
800 }
801
802 {
803 // Test `zig init-exe`.
779 // Test `zig init`.
804780 const tmp_path = b.makeTempPath();
805 const init_exe = b.addSystemCommand(&.{ b.zig_exe, "init-exe" });
781 const init_exe = b.addSystemCommand(&.{ b.zig_exe, "init" });
806782 init_exe.setCwd(.{ .cwd_relative = tmp_path });
807 init_exe.setName("zig init-exe");
783 init_exe.setName("zig init");
808784 init_exe.expectStdOutEqual("");
809 init_exe.expectStdErrEqual("info: Created build.zig\n" ++
810 "info: Created src" ++ s ++ "main.zig\n" ++
811 "info: Next, try `zig build --help` or `zig build run`\n");
785 init_exe.expectStdErrEqual("info: created build.zig\n" ++
786 "info: created build.zig.zon\n" ++
787 "info: created src" ++ s ++ "main.zig\n" ++
788 "info: created src" ++ s ++ "root.zig\n" ++
789 "info: see `zig build --help` for a menu of options\n");
812790
813791 // Test missing output path.
814792 const bad_out_arg = "-femit-bin=does" ++ s ++ "not" ++ s ++ "exist" ++ s ++ "foo.exe";