Tiny C++ integration
This commit is contained in:
parent
0b928835a0
commit
328b907506
@ -171,7 +171,7 @@ const CSourceKind = enum {
|
||||
cpp,
|
||||
};
|
||||
|
||||
pub fn compileCSourceFile(context: *const Context, arguments: [][*:0]u8, kind: CSourceKind) !void {
|
||||
fn compileMusl(context: *const Context) MuslContext{
|
||||
const musl = try MuslContext.init(context);
|
||||
var exists = true;
|
||||
var dir = std.fs.cwd().openDir(musl.global_cache_dir, .{}) catch b: {
|
||||
@ -260,13 +260,37 @@ pub fn compileCSourceFile(context: *const Context, arguments: [][*:0]u8, kind: C
|
||||
}
|
||||
}
|
||||
|
||||
return musl;
|
||||
}
|
||||
|
||||
pub fn compileCSourceFile(context: *const Context, arguments: [][*:0]u8, kind: CSourceKind) !void {
|
||||
var clang_args = UnpinnedArray([]const u8){};
|
||||
try clang_args.append(context.my_allocator, context.executable_absolute_path);
|
||||
try clang_args.append(context.my_allocator, "clang");
|
||||
|
||||
const Mode = enum{
|
||||
object,
|
||||
link,
|
||||
};
|
||||
const mode: Mode = for (arguments) |argz| {
|
||||
const arg = span(argz);
|
||||
if (byte_equal(arg, "-c")) break .object;
|
||||
} else .link;
|
||||
_ = mode; // autofix
|
||||
|
||||
if (kind == .cpp) {
|
||||
try clang_args.append(context.my_allocator, "-nostdinc++");
|
||||
|
||||
switch (@import("builtin").os.tag) {
|
||||
.linux => {
|
||||
switch (@import("builtin").abi) {
|
||||
.gnu => {
|
||||
try clang_args.append_slice(context.my_allocator, &.{
|
||||
"-isystem", "/usr/include/c++/13.2.1",
|
||||
"-isystem", "/usr/include/c++/13.2.1/x86_64-pc-linux-gnu",
|
||||
});
|
||||
},
|
||||
.musl => {
|
||||
try clang_args.append_slice(context.my_allocator, &.{
|
||||
"-isystem", try context.pathFromCompiler("lib/libcxx/include"),
|
||||
"-isystem", try context.pathFromCompiler("lib/libcxxabi/include"),
|
||||
@ -277,9 +301,22 @@ pub fn compileCSourceFile(context: *const Context, arguments: [][*:0]u8, kind: C
|
||||
"-D_LIBCPP_ABI_VERSION=1",
|
||||
"-D_LIBCPP_ABI_NAMESPACE=__1",
|
||||
});
|
||||
switch (@import("builtin").os.tag) {
|
||||
.linux => {},
|
||||
.macos => {},
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.macos => {
|
||||
try clang_args.append_slice(context.my_allocator, &.{
|
||||
"-isystem", try context.pathFromCompiler("lib/libcxx/include"),
|
||||
"-isystem", try context.pathFromCompiler("lib/libcxxabi/include"),
|
||||
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
|
||||
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
|
||||
"-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS",
|
||||
"-D_LIBCPP_PSTL_CPU_BACKEND_SERIAL",
|
||||
"-D_LIBCPP_ABI_VERSION=1",
|
||||
"-D_LIBCPP_ABI_NAMESPACE=__1",
|
||||
});
|
||||
},
|
||||
else => @compileError("Operating system not supported"),
|
||||
}
|
||||
}
|
||||
@ -289,16 +326,26 @@ pub fn compileCSourceFile(context: *const Context, arguments: [][*:0]u8, kind: C
|
||||
|
||||
switch (@import("builtin").os.tag) {
|
||||
.linux => {
|
||||
switch (@import("builtin").abi) {
|
||||
.gnu => {
|
||||
try clang_args.append_slice(context.my_allocator, &.{
|
||||
"-isystem", "/usr/lib/clang/17/include",
|
||||
"-isystem", "/usr/include",
|
||||
"-isystem", "/usr/include/linux",
|
||||
});
|
||||
},
|
||||
.musl => {
|
||||
try clang_args.append_slice(context.my_allocator, &.{
|
||||
"-isystem", try context.pathFromCompiler("lib/include"),
|
||||
"-isystem", try context.pathFromCompiler("lib/libc/include/x86_64-linux-gnu"),
|
||||
"-isystem", try context.pathFromCompiler("lib/libc/include/generic-glibc"),
|
||||
"-isystem", try context.pathFromCompiler("lib/libc/include/x86-linux-any"),
|
||||
"-isystem", try context.pathFromCompiler("lib/libc/include/any-linux-any"),
|
||||
"-isystem", "/usr/include",
|
||||
"-isystem", "/usr/include/linux",
|
||||
});
|
||||
},
|
||||
else => @compileError("Abi not supported"),
|
||||
}
|
||||
},
|
||||
.macos => {
|
||||
try clang_args.append_slice(context.my_allocator, &.{
|
||||
"-iframework", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks",
|
||||
@ -310,6 +357,24 @@ pub fn compileCSourceFile(context: *const Context, arguments: [][*:0]u8, kind: C
|
||||
}
|
||||
}
|
||||
|
||||
if (kind == .cpp) {
|
||||
switch (@import("builtin").os.tag) {
|
||||
.linux => {
|
||||
switch (@import("builtin").abi) {
|
||||
.gnu => {
|
||||
try clang_args.append(context.my_allocator, "-lstdc++");
|
||||
},
|
||||
.musl => {
|
||||
unreachable;
|
||||
},
|
||||
else => @compileError("Abi not supported"),
|
||||
}
|
||||
},
|
||||
.macos => unreachable,
|
||||
else => @compileError("OS not supported"),
|
||||
}
|
||||
}
|
||||
|
||||
for (arguments) |arg| {
|
||||
try clang_args.append(context.my_allocator, span(arg));
|
||||
}
|
||||
@ -319,43 +384,43 @@ pub fn compileCSourceFile(context: *const Context, arguments: [][*:0]u8, kind: C
|
||||
unreachable;
|
||||
}
|
||||
|
||||
const output_object_file = "nat/main.o";
|
||||
// const output_object_file = "nat/main.o";
|
||||
// const exit_code = try clangMain(context.allocator, &.{ context.executable_absolute_path, "--no-default-config", "-target", "x86_64-unknown-linux-musl", "-nostdinc", "-fno-spell-checking", "-isystem", "lib/include", "-isystem", "lib/libc/include/x86_64-linux-musl", "-isystem", "lib/libc/include/generic-musl", "-isystem", "lib/libc/include/x86-linux-any", "-isystem", "lib/libc/include/any-linux-any", "-c", argument, "-o", output_object_file });
|
||||
// if (exit_code != 0) {
|
||||
// unreachable;
|
||||
// }
|
||||
|
||||
const link = false;
|
||||
if (link) {
|
||||
var lld_args = UnpinnedArray([*:0]const u8){};
|
||||
try lld_args.append(context.my_allocator, "ld.lld");
|
||||
try lld_args.append(context.my_allocator, "--error-limit=0");
|
||||
try lld_args.append(context.my_allocator, "--entry");
|
||||
try lld_args.append(context.my_allocator, "_start");
|
||||
try lld_args.append(context.my_allocator, "-z");
|
||||
try lld_args.append(context.my_allocator, "stack-size=16777216");
|
||||
try lld_args.append(context.my_allocator, "--image-base=16777216");
|
||||
try lld_args.append(context.my_allocator, "-m");
|
||||
try lld_args.append(context.my_allocator, "elf_x86_64");
|
||||
try lld_args.append(context.my_allocator, "-static");
|
||||
try lld_args.append(context.my_allocator, "-o");
|
||||
try lld_args.append(context.my_allocator, "nat/main");
|
||||
try lld_args.append(context.my_allocator, try std.mem.joinZ(context.allocator, "", &.{ musl.global_cache_dir, "crt1.o" }));
|
||||
try lld_args.append(context.my_allocator, try std.mem.joinZ(context.allocator, "", &.{ musl.global_cache_dir, "crti.o" }));
|
||||
try lld_args.append(context.my_allocator, output_object_file);
|
||||
try lld_args.append(context.my_allocator, "--as-needed");
|
||||
try lld_args.append(context.my_allocator, try std.mem.joinZ(context.allocator, "", &.{ musl.global_cache_dir, "libc.a" }));
|
||||
try lld_args.append(context.my_allocator, try std.mem.joinZ(context.allocator, "", &.{ musl.global_cache_dir, "crtn.o" }));
|
||||
|
||||
var stdout_ptr: [*]const u8 = undefined;
|
||||
var stdout_len: usize = 0;
|
||||
var stderr_ptr: [*]const u8 = undefined;
|
||||
var stderr_len: usize = 0;
|
||||
const link_result = llvm.bindings.NativityLLDLinkELF(lld_args.pointer, lld_args.length, &stdout_ptr, &stdout_len, &stderr_ptr, &stderr_len);
|
||||
if (!link_result) {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
// const link = false;
|
||||
// if (link) {
|
||||
// var lld_args = UnpinnedArray([*:0]const u8){};
|
||||
// try lld_args.append(context.my_allocator, "ld.lld");
|
||||
// try lld_args.append(context.my_allocator, "--error-limit=0");
|
||||
// try lld_args.append(context.my_allocator, "--entry");
|
||||
// try lld_args.append(context.my_allocator, "_start");
|
||||
// try lld_args.append(context.my_allocator, "-z");
|
||||
// try lld_args.append(context.my_allocator, "stack-size=16777216");
|
||||
// try lld_args.append(context.my_allocator, "--image-base=16777216");
|
||||
// try lld_args.append(context.my_allocator, "-m");
|
||||
// try lld_args.append(context.my_allocator, "elf_x86_64");
|
||||
// try lld_args.append(context.my_allocator, "-static");
|
||||
// try lld_args.append(context.my_allocator, "-o");
|
||||
// try lld_args.append(context.my_allocator, "nat/main");
|
||||
// try lld_args.append(context.my_allocator, try std.mem.joinZ(context.allocator, "", &.{ musl.global_cache_dir, "crt1.o" }));
|
||||
// try lld_args.append(context.my_allocator, try std.mem.joinZ(context.allocator, "", &.{ musl.global_cache_dir, "crti.o" }));
|
||||
// try lld_args.append(context.my_allocator, output_object_file);
|
||||
// try lld_args.append(context.my_allocator, "--as-needed");
|
||||
// try lld_args.append(context.my_allocator, try std.mem.joinZ(context.allocator, "", &.{ musl.global_cache_dir, "libc.a" }));
|
||||
// try lld_args.append(context.my_allocator, try std.mem.joinZ(context.allocator, "", &.{ musl.global_cache_dir, "crtn.o" }));
|
||||
//
|
||||
// var stdout_ptr: [*]const u8 = undefined;
|
||||
// var stdout_len: usize = 0;
|
||||
// var stderr_ptr: [*]const u8 = undefined;
|
||||
// var stderr_len: usize = 0;
|
||||
// const link_result = llvm.bindings.NativityLLDLinkELF(lld_args.pointer, lld_args.length, &stdout_ptr, &stdout_len, &stderr_ptr, &stderr_len);
|
||||
// if (!link_result) {
|
||||
// unreachable;
|
||||
// }
|
||||
// }
|
||||
// const thread = try std.Thread.spawn(.{}, clang_job, .{args});
|
||||
// thread.join();
|
||||
}
|
||||
|
@ -26,7 +26,8 @@ pub fn build(b: *std.Build) !void {
|
||||
const print_stack_trace = b.option(bool, "print_stack_trace", "This option enables printing stack traces inside the compiler") orelse is_ci or os == .macos;
|
||||
const native_target = b.resolveTargetQuery(.{});
|
||||
const optimization = b.standardOptimizeOption(.{});
|
||||
const static = b.option(bool, "static", "This option enables the compiler to be built statically") orelse true;
|
||||
const use_debug = b.option(bool, "use_debug", "This option enables the LLVM debug build in the development PC") orelse false;
|
||||
const static = b.option(bool, "static", "This option enables the compiler to be built statically") orelse use_debug;
|
||||
const compiler_options = b.addOptions();
|
||||
compiler_options.addOption(bool, "print_stack_trace", print_stack_trace);
|
||||
|
||||
@ -384,7 +385,6 @@ pub fn build(b: *std.Build) !void {
|
||||
|
||||
break :blk llvm_directory.items;
|
||||
} else {
|
||||
const use_debug = b.option(bool, "use_debug", "This option enables the LLVM debug build in the development PC") orelse false;
|
||||
break :blk switch (use_debug) {
|
||||
true => "../llvm-17-static-debug",
|
||||
false => "../llvm-17-static-release",
|
||||
|
@ -53,6 +53,7 @@ fn runStandalone(allocator: Allocator, args: struct {
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{ "zig-out/bin/nat", if (args.is_test) "test" else "exe", "-main_source_file", source_file_path },
|
||||
.max_output_bytes = std.math.maxInt(u64),
|
||||
});
|
||||
ran_compilation_count += 1;
|
||||
|
||||
@ -82,6 +83,7 @@ fn runStandalone(allocator: Allocator, args: struct {
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{test_path},
|
||||
.max_output_bytes = std.math.maxInt(u64),
|
||||
});
|
||||
ran_test_count += 1;
|
||||
const test_result: TestError!bool = switch (test_run.term) {
|
||||
@ -134,6 +136,7 @@ fn runStandaloneTests(allocator: Allocator) !void {
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{ "zig-out/bin/nat", "exe", "-main_source_file", source_file_path },
|
||||
.max_output_bytes = std.math.maxInt(u64),
|
||||
});
|
||||
ran_compilation_count += 1;
|
||||
|
||||
@ -163,6 +166,7 @@ fn runStandaloneTests(allocator: Allocator) !void {
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{test_path},
|
||||
.max_output_bytes = std.math.maxInt(u64),
|
||||
});
|
||||
ran_test_count += 1;
|
||||
const test_result: TestError!bool = switch (test_run.term) {
|
||||
@ -221,6 +225,7 @@ fn runBuildTests(allocator: Allocator) !void {
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{ compiler_realpath, "build" },
|
||||
.max_output_bytes = std.math.maxInt(u64),
|
||||
});
|
||||
|
||||
ran_compilation_count += 1;
|
||||
@ -251,6 +256,7 @@ fn runBuildTests(allocator: Allocator) !void {
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{test_path},
|
||||
.max_output_bytes = std.math.maxInt(u64),
|
||||
});
|
||||
ran_test_count += 1;
|
||||
const test_result: TestError!bool = switch (test_run.term) {
|
||||
@ -288,33 +294,14 @@ fn runBuildTests(allocator: Allocator) !void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
fn runStdTests(allocator: Allocator) !void {
|
||||
var errors = false;
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
const allocator = arena.allocator();
|
||||
runStandalone(allocator, .{
|
||||
.directory_path = "test/standalone",
|
||||
.group_name = "STANDALONE",
|
||||
.is_test = false,
|
||||
}) catch {
|
||||
errors = true;
|
||||
};
|
||||
runBuildTests(allocator) catch {
|
||||
errors = true;
|
||||
};
|
||||
runStandalone(allocator, .{
|
||||
.directory_path = "test/tests",
|
||||
.group_name = "TEST EXECUTABLE",
|
||||
.is_test = true,
|
||||
}) catch {
|
||||
errors = true;
|
||||
};
|
||||
|
||||
std.debug.print("std... ", .{});
|
||||
|
||||
const result = try std.ChildProcess.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = &.{ "zig-out/bin/nat", "test", "-main_source_file", "lib/std/std.nat", "-name", "std" },
|
||||
.max_output_bytes = std.math.maxInt(u64),
|
||||
});
|
||||
const compilation_result: TestError!bool = switch (result.term) {
|
||||
.Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code,
|
||||
@ -341,6 +328,7 @@ pub fn main() !void {
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{"nat/std"},
|
||||
.max_output_bytes = std.math.maxInt(u64),
|
||||
});
|
||||
const test_result: TestError!bool = switch (test_run.term) {
|
||||
.Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code,
|
||||
@ -362,6 +350,171 @@ pub fn main() !void {
|
||||
}
|
||||
}
|
||||
|
||||
if (errors) return error.fail;
|
||||
}
|
||||
|
||||
fn runCmakeTests(allocator: Allocator) !void {
|
||||
var errors = false;
|
||||
const original_dir = try std.fs.cwd().realpathAlloc(allocator, ".");
|
||||
const cc_dir = try std.fs.cwd().openDir("test/cc", .{
|
||||
.iterate = true,
|
||||
});
|
||||
|
||||
const cc_dir_path = try cc_dir.realpathAlloc(allocator, ".");
|
||||
try std.posix.chdir(cc_dir_path);
|
||||
|
||||
var cc_dir_iterator = cc_dir.iterate();
|
||||
while (try cc_dir_iterator.next()) |cc_entry| {
|
||||
switch (cc_entry.kind) {
|
||||
.directory => {
|
||||
std.debug.print("{s}...\n", .{cc_entry.name});
|
||||
try std.posix.chdir(cc_entry.name);
|
||||
try std.fs.cwd().deleteTree("build");
|
||||
try std.fs.cwd().makeDir("build");
|
||||
try std.posix.chdir("build");
|
||||
|
||||
const cmake = try std.ChildProcess.run(.{
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{
|
||||
"cmake",
|
||||
"..",
|
||||
"-G", "Unix Makefiles",
|
||||
"-DCMAKE_VERBOSE_MAKEFILE=On",
|
||||
try std.mem.concat(allocator, u8, &.{ "-DCMAKE_C_COMPILER=", "nat;cc" }),
|
||||
try std.mem.concat(allocator, u8, &.{ "-DCMAKE_CXX_COMPILER=", "nat;c++" }),
|
||||
try std.mem.concat(allocator, u8, &.{ "-DCMAKE_ASM_COMPILER=", "nat;cc" }),
|
||||
},
|
||||
.max_output_bytes = std.math.maxInt(u64),
|
||||
});
|
||||
const cmake_result: TestError!bool = switch (cmake.term) {
|
||||
.Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code,
|
||||
.Signal => error.signaled,
|
||||
.Stopped => error.stopped,
|
||||
.Unknown => error.unknown,
|
||||
};
|
||||
|
||||
const cmake_success = cmake_result catch b: {
|
||||
errors = true;
|
||||
break :b false;
|
||||
};
|
||||
|
||||
if (!cmake_success) {
|
||||
if (cmake.stdout.len > 0) {
|
||||
std.debug.print("STDOUT:\n\n{s}\n\n", .{cmake.stdout});
|
||||
}
|
||||
if (cmake.stderr.len > 0) {
|
||||
std.debug.print("STDERR:\n\n{s}\n\n", .{cmake.stderr});
|
||||
}
|
||||
}
|
||||
|
||||
var success = cmake_success;
|
||||
if (success) {
|
||||
const ninja = try std.ChildProcess.run(.{
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{
|
||||
"make"
|
||||
},
|
||||
.max_output_bytes = std.math.maxInt(u64),
|
||||
});
|
||||
const ninja_result: TestError!bool = switch (ninja.term) {
|
||||
.Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code,
|
||||
.Signal => error.signaled,
|
||||
.Stopped => error.stopped,
|
||||
.Unknown => error.unknown,
|
||||
};
|
||||
|
||||
const ninja_success = ninja_result catch b: {
|
||||
errors = true;
|
||||
break :b false;
|
||||
};
|
||||
if (!ninja_success) {
|
||||
if (ninja.stdout.len > 0) {
|
||||
std.debug.print("STDOUT:\n\n{s}\n\n", .{ninja.stdout});
|
||||
}
|
||||
if (ninja.stderr.len > 0) {
|
||||
std.debug.print("STDERR:\n\n{s}\n\n", .{ninja.stderr});
|
||||
}
|
||||
}
|
||||
|
||||
success = success and ninja_success;
|
||||
|
||||
if (success) {
|
||||
const run = try std.ChildProcess.run(.{
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{
|
||||
try std.mem.concat(allocator, u8, &.{ "./", cc_entry.name}),
|
||||
},
|
||||
.max_output_bytes = std.math.maxInt(u64),
|
||||
});
|
||||
const run_result: TestError!bool = switch (run.term) {
|
||||
.Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code,
|
||||
.Signal => error.signaled,
|
||||
.Stopped => error.stopped,
|
||||
.Unknown => error.unknown,
|
||||
};
|
||||
|
||||
const run_success = run_result catch b: {
|
||||
errors = true;
|
||||
break :b false;
|
||||
};
|
||||
|
||||
if (run.stdout.len > 0) {
|
||||
std.debug.print("STDOUT:\n\n{s}\n\n", .{run.stdout});
|
||||
}
|
||||
if (run.stderr.len > 0) {
|
||||
std.debug.print("STDERR:\n\n{s}\n\n", .{run.stderr});
|
||||
}
|
||||
|
||||
success = success and run_success;
|
||||
}
|
||||
}
|
||||
|
||||
std.debug.print("[TEST {s}]\n", .{if (success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"});
|
||||
},
|
||||
else => std.debug.panic("Entry {s} is a {s}", .{cc_entry.name, @tagName(cc_entry.kind)}),
|
||||
}
|
||||
|
||||
try std.posix.chdir(cc_dir_path);
|
||||
}
|
||||
|
||||
try std.posix.chdir(original_dir);
|
||||
|
||||
if (errors) {
|
||||
return error.fail;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var errors = false;
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
const allocator = arena.allocator();
|
||||
runStandalone(allocator, .{
|
||||
.directory_path = "test/standalone",
|
||||
.group_name = "STANDALONE",
|
||||
.is_test = false,
|
||||
}) catch {
|
||||
errors = true;
|
||||
};
|
||||
runBuildTests(allocator) catch {
|
||||
errors = true;
|
||||
};
|
||||
runStandalone(allocator, .{
|
||||
.directory_path = "test/tests",
|
||||
.group_name = "TEST EXECUTABLE",
|
||||
.is_test = true,
|
||||
}) catch {
|
||||
errors = true;
|
||||
};
|
||||
|
||||
runStdTests(allocator) catch {
|
||||
errors = true;
|
||||
};
|
||||
|
||||
try runCmakeTests(allocator);
|
||||
|
||||
if (errors) {
|
||||
return error.fail;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <stdio.h>
|
||||
int main()
|
||||
{
|
||||
printf("Hello world\n");
|
||||
printf("Hello \n");
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user