mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 03:51:18 +00:00
Implement passing arguments to program via compile-run (#1296)
This commit is contained in:
6
.github/workflows/main.yml
vendored
6
.github/workflows/main.yml
vendored
@@ -43,6 +43,7 @@ jobs:
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\ls.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\process.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\args.c3 -- foo -bar "baz baz"
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples\constants.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run msvc_stack.c3
|
||||
|
||||
@@ -132,6 +133,7 @@ jobs:
|
||||
../build/c3c compile-run --print-linking examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run --print-linking examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run --print-linking examples/load_world.c3
|
||||
../build/c3c compile-run --print-linking examples/args.c3 -- foo -bar "baz baz"
|
||||
../build/c3c compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
|
||||
|
||||
- name: Build testproject
|
||||
@@ -188,6 +190,7 @@ jobs:
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||
../build/c3c compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
|
||||
- name: Build testproject
|
||||
run: |
|
||||
@@ -304,6 +307,7 @@ jobs:
|
||||
../build/c3c compile-run examples/ls.c3
|
||||
../build/c3c compile-run --linker=builtin linux_stack.c3
|
||||
../build/c3c compile-run linux_stack.c3
|
||||
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
@@ -445,6 +449,7 @@ jobs:
|
||||
../build/c3c compile-run examples/process.c3
|
||||
../build/c3c compile-run --linker=builtin linux_stack.c3
|
||||
../build/c3c compile-run linux_stack.c3
|
||||
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
@@ -524,6 +529,7 @@ jobs:
|
||||
../build/c3c compile-run examples/process.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
../build/c3c compile-run -O5 examples/load_world.c3
|
||||
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -67,3 +67,6 @@ out/
|
||||
|
||||
/cmake-build-debug/
|
||||
/cmake-build-release/
|
||||
|
||||
# Emacs files
|
||||
TAGS
|
||||
|
||||
@@ -252,6 +252,8 @@ add_executable(c3c
|
||||
src/compiler/codegen_general.c
|
||||
src/compiler/compiler.c
|
||||
src/compiler/compiler.h
|
||||
src/compiler/subprocess.c
|
||||
src/compiler/subprocess.h
|
||||
src/compiler/context.c
|
||||
src/compiler/copying.c
|
||||
src/compiler/diagnostics.c
|
||||
@@ -369,7 +371,8 @@ endif()
|
||||
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/src/" "${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||
"${CMAKE_SOURCE_DIR}/src/"
|
||||
"${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||
|
||||
|
||||
target_include_directories(c3c_wrappers PRIVATE
|
||||
@@ -424,7 +427,7 @@ else()
|
||||
message(STATUS "using gcc/clang warning switches")
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
if (NOT LLVM_ENABLE_RTTI)
|
||||
target_compile_options(c3c_wrappers PUBLIC -fno-rtti)
|
||||
target_compile_options(c3c_wrappers PRIVATE -fno-rtti)
|
||||
endif()
|
||||
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
||||
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||
|
||||
6
resources/examples/args.c3
Normal file
6
resources/examples/args.c3
Normal file
@@ -0,0 +1,6 @@
|
||||
import std::io;
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
io::printn(args);
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#define MAX_LIB_DIRS 1024
|
||||
#define MAX_FILES 2048
|
||||
#define MAX_ARGS 2048
|
||||
#define MAX_INCLUDES 2048
|
||||
#define MAX_THREADS 0xFFFF
|
||||
#define DEFAULT_SYMTAB_SIZE (256 * 1024)
|
||||
@@ -373,6 +374,7 @@ typedef struct BuildOptions_
|
||||
int build_threads;
|
||||
const char **libraries_to_fetch;
|
||||
const char **files;
|
||||
const char **args;
|
||||
const char **feature_names;
|
||||
const char **removed_feature_names;
|
||||
const char *output_name;
|
||||
@@ -555,6 +557,7 @@ typedef struct
|
||||
LinkerType linker_type;
|
||||
uint32_t symtab_size;
|
||||
uint32_t switchrange_max_size;
|
||||
const char **args;
|
||||
const char *panicfn;
|
||||
const char *benchfn;
|
||||
const char *testfn;
|
||||
@@ -652,6 +655,7 @@ static BuildTarget default_build_target = {
|
||||
BuildOptions parse_arguments(int argc, const char *argv[]);
|
||||
ArchOsTarget arch_os_target_from_string(const char *target);
|
||||
bool command_accepts_files(CompilerCommand command);
|
||||
bool command_passes_args(CompilerCommand command);
|
||||
void update_build_target_with_opt_level(BuildTarget *target, OptimizationSetting level);
|
||||
void create_project(BuildOptions *build_options);
|
||||
void create_library(BuildOptions *build_options);
|
||||
|
||||
@@ -70,7 +70,7 @@ static void usage(void)
|
||||
PRINTF(" directives [<target>] Generate documentation for the target.");
|
||||
PRINTF(" bench [<target>] Benchmark a target.");
|
||||
PRINTF(" clean-run [<target>] Clean, then run the target.");
|
||||
PRINTF(" compile-run <file1> [<file2> ...] Compile files then immediately run the result.");
|
||||
PRINTF(" compile-run <file1> [<file2> ...] [-- [<arg1> ...]] Compile files then immediately run the result.");
|
||||
PRINTF(" compile-only <file1> [<file2> ...] Compile files but do not perform linking.");
|
||||
PRINTF(" compile-benchmark <file1> [<file2> ...] Compile files into an executable and run benchmarks.");
|
||||
PRINTF(" compile-test <file1> [<file2> ...] Compile files into an executable and run unit tests.");
|
||||
@@ -250,6 +250,16 @@ void append_file(BuildOptions *build_options)
|
||||
vec_add(build_options->files, current_arg);
|
||||
}
|
||||
|
||||
void append_arg(BuildOptions *build_options)
|
||||
{
|
||||
if (vec_size(build_options->args) == MAX_ARGS)
|
||||
{
|
||||
EOUTPUT("Max %d args may be specified.", MAX_ARGS);
|
||||
exit_compiler(EXIT_FAILURE);
|
||||
}
|
||||
vec_add(build_options->args, current_arg);
|
||||
}
|
||||
|
||||
static bool arg_match(const char *candidate)
|
||||
{
|
||||
return strcmp(current_arg, candidate) == 0;
|
||||
@@ -1189,9 +1199,19 @@ BuildOptions parse_arguments(int argc, const char *argv[])
|
||||
build_options.severity[i] = DIAG_ERROR;
|
||||
}
|
||||
|
||||
bool collecting_args = false;
|
||||
for (arg_index = 1; arg_index < arg_count; arg_index++)
|
||||
{
|
||||
current_arg = args[arg_index];
|
||||
if (collecting_args) {
|
||||
append_arg(&build_options);
|
||||
continue;
|
||||
}
|
||||
if (command_passes_args(build_options.command) && arg_match("--"))
|
||||
{
|
||||
collecting_args = true;
|
||||
continue;
|
||||
}
|
||||
if (current_arg[0] == '-')
|
||||
{
|
||||
parse_option(&build_options);
|
||||
|
||||
@@ -92,6 +92,38 @@ bool command_accepts_files(CompilerCommand command)
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
bool command_passes_args(CompilerCommand command)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case COMMAND_COMPILE_RUN:
|
||||
return true;
|
||||
case COMMAND_COMPILE:
|
||||
case COMMAND_COMPILE_ONLY:
|
||||
case COMMAND_DYNAMIC_LIB:
|
||||
case COMMAND_STATIC_LIB:
|
||||
case COMMAND_COMPILE_BENCHMARK:
|
||||
case COMMAND_COMPILE_TEST:
|
||||
case COMMAND_UNIT_TEST:
|
||||
case COMMAND_MISSING:
|
||||
case COMMAND_GENERATE_HEADERS:
|
||||
case COMMAND_INIT:
|
||||
case COMMAND_INIT_LIB:
|
||||
case COMMAND_BUILD:
|
||||
case COMMAND_RUN:
|
||||
case COMMAND_CLEAN_RUN:
|
||||
case COMMAND_CLEAN:
|
||||
case COMMAND_DIST:
|
||||
case COMMAND_DOCS:
|
||||
case COMMAND_BENCH:
|
||||
case COMMAND_PRINT_SYNTAX:
|
||||
case COMMAND_BENCHMARK:
|
||||
case COMMAND_TEST:
|
||||
case COMMAND_VENDOR_FETCH:
|
||||
return false;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
void update_build_target_with_opt_level(BuildTarget *target, OptimizationSetting level)
|
||||
{
|
||||
@@ -213,6 +245,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
|
||||
case COMMAND_CLEAN_RUN:
|
||||
target->run_after_compile = true;
|
||||
target->delete_after_run = options->run_once;
|
||||
target->args = options->args;
|
||||
break;
|
||||
case COMMAND_COMPILE_ONLY:
|
||||
target->type = TARGET_TYPE_OBJECT_FILES;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
#include "c3_llvm.h"
|
||||
#include <errno.h>
|
||||
|
||||
#define MAX_OUTPUT_FILES 1000000
|
||||
#define MAX_MODULES 100000
|
||||
@@ -649,39 +650,20 @@ void compiler_compile(void)
|
||||
scratch_buffer_append(name);
|
||||
}
|
||||
name = scratch_buffer_to_string();
|
||||
printf("Launching %s...\n", name);
|
||||
|
||||
int ret = system(name);
|
||||
printf("Launching %s", name);
|
||||
for (uint32_t i = 0; i < vec_size(active_target.args); ++i) {
|
||||
printf(" %s", active_target.args[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
int ret = run_subprocess(name, active_target.args);
|
||||
if (active_target.delete_after_run)
|
||||
{
|
||||
file_delete_file(name);
|
||||
}
|
||||
|
||||
#if PLATFORM_POSIX
|
||||
if (WIFEXITED(ret))
|
||||
{
|
||||
int status = WEXITSTATUS(ret);
|
||||
printf("Program completed with exit code %d.\n", status);
|
||||
if (status != 0) exit(status);
|
||||
}
|
||||
else if (WIFSIGNALED(ret))
|
||||
{
|
||||
printf("Program interrupted by signal %d.\n", WTERMSIG(ret));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else if (WIFSTOPPED(ret))
|
||||
{
|
||||
printf("Program stopped by signal %d.\n", WSTOPSIG(ret));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Program finished with unexpected code %d.\n", ret);
|
||||
}
|
||||
#else
|
||||
if (ret < 0) exit_compiler(EXIT_FAILURE);
|
||||
printf("Program completed with exit code %d.\n", ret);
|
||||
if (ret != 0) exit(ret);
|
||||
#endif
|
||||
if (ret != 0) exit_compiler(ret);
|
||||
}
|
||||
}
|
||||
else if (output_static)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "enums.h"
|
||||
#include "target.h"
|
||||
#include "utils/malloc.h"
|
||||
#include "subprocess.h"
|
||||
#include <float.h>
|
||||
|
||||
typedef double Real;
|
||||
|
||||
141
src/compiler/subprocess.c
Normal file
141
src/compiler/subprocess.c
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "../utils/common.h"
|
||||
#include "../utils/lib.h"
|
||||
#include <errno.h>
|
||||
#include "subprocess.h"
|
||||
|
||||
#if PLATFORM_POSIX
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
int run_subprocess(const char *name, const char **args)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
// https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output
|
||||
|
||||
STARTUPINFOA siStartInfo;
|
||||
ZeroMemory(&siStartInfo, sizeof(siStartInfo));
|
||||
siStartInfo.cb = sizeof(STARTUPINFO);
|
||||
// NOTE: theoretically setting NULL to std handles should not be a problem
|
||||
// https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior
|
||||
// TODO: check for errors in GetStdHandle
|
||||
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
|
||||
|
||||
PROCESS_INFORMATION piProcInfo;
|
||||
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
|
||||
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_printf("%s", name);
|
||||
for (uint32_t i = 0; i < vec_size(args); i++) {
|
||||
scratch_buffer_append_char(' ');
|
||||
|
||||
bool need_quoting = strpbrk(args[i], "\t\v ") != NULL || args[i][0] == 0;
|
||||
if (need_quoting) scratch_buffer_append_char('"');
|
||||
for (int j = 0; '\0' != args[i][j]; j++) {
|
||||
switch (args[i][j]) {
|
||||
default:
|
||||
break;
|
||||
case '\\':
|
||||
if (args[i][j + 1] == '"') {
|
||||
scratch_buffer_append_char('\\');
|
||||
}
|
||||
|
||||
break;
|
||||
case '"':
|
||||
scratch_buffer_append_char('\\');
|
||||
break;
|
||||
}
|
||||
|
||||
scratch_buffer_append_char(args[i][j]);
|
||||
}
|
||||
|
||||
if (need_quoting) scratch_buffer_append_char('"');
|
||||
}
|
||||
|
||||
BOOL bSuccess = CreateProcessA(
|
||||
NULL,
|
||||
scratch_buffer_to_string(),
|
||||
NULL,
|
||||
NULL,
|
||||
TRUE,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
&siStartInfo,
|
||||
&piProcInfo);
|
||||
|
||||
if (!bSuccess) {
|
||||
eprintf("Could not create child process: %lu", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
CloseHandle(piProcInfo.hThread);
|
||||
|
||||
DWORD result = WaitForSingleObject(
|
||||
piProcInfo.hProcess, // HANDLE hHandle,
|
||||
INFINITE // DWORD dwMilliseconds
|
||||
);
|
||||
|
||||
if (result == WAIT_FAILED) {
|
||||
eprintf("Could not wait on child process: %lu", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
DWORD exit_status;
|
||||
if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_status)) {
|
||||
eprintf("Could not get process exit code: %lu", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
CloseHandle(piProcInfo.hProcess);
|
||||
|
||||
return exit_status;
|
||||
#else
|
||||
pid_t cpid = fork();
|
||||
if (cpid < 0) {
|
||||
eprintf("Could not fork child process %s: %s", name, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cpid == 0) {
|
||||
const char **args_null = NULL;
|
||||
vec_add(args_null, name);
|
||||
for (uint32_t i = 0; i < vec_size(args); ++i) {
|
||||
vec_add(args_null, args[i]);
|
||||
}
|
||||
vec_add(args_null, NULL);
|
||||
|
||||
if (execvp(name, (char * const*)args_null) < 0) {
|
||||
eprintf("Could not exec child process %s: %s", name, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int wstatus = 0;
|
||||
if (waitpid(cpid, &wstatus, 0) < 0) {
|
||||
eprintf("Could not wait on %s (pid %d): %s", name, cpid, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WIFEXITED(wstatus)) {
|
||||
return WEXITSTATUS(wstatus);
|
||||
}
|
||||
|
||||
if (WIFSIGNALED(wstatus)) {
|
||||
eprintf("Program interrupted by signal %d.\n", WTERMSIG(wstatus));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return cpid;
|
||||
#endif
|
||||
}
|
||||
3
src/compiler/subprocess.h
Normal file
3
src/compiler/subprocess.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int run_subprocess(const char *name, const char **args);
|
||||
Reference in New Issue
Block a user