/* Copyright (c) 2026 Juhapekka Piiroinen * All Rights Reserved. * AmoosedWeb project: https://amoosed.com * * Preview example only. A public compiler C binary is not distributed yet. * Usage: native-c-build ABSOLUTE_PROJECT ABSOLUTE_RUNTIME ABSOLUTE_OUTPUT */ #include "AmoosedWeb/CompilerPreviewC.h" #include #include #include static void AMWC_PREVIEW_CALL on_build_progress(uint32_t phase, void* context) { FILE* stream = (FILE*)context; if (phase == AMWC_PREVIEW_BUILD_SITE_STAGED) fputs("Staged site ready for verification.\n", stream); else if (phase == AMWC_PREVIEW_BUILD_SITE_VERIFIED) fputs("Staged site verified.\n", stream); else if (phase == AMWC_PREVIEW_BUILD_CREDENTIALS_PUBLISHED) fputs("Private credentials published; site publication follows.\n", stream); } static void clear_bytes(void* memory, size_t length) { volatile unsigned char* bytes = (volatile unsigned char*)memory; for (size_t i = 0; i < length; ++i) bytes[i] = 0; } static int hex_digit(int ch) { if (ch >= '0' && ch <= '9') return ch - '0'; if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; return -1; } static int read_key(const char* path, unsigned char key[32]) { char text[67] = {0}; FILE* file = fopen(path, "rb"); if (!file) return 0; const size_t length = fread(text, 1, sizeof(text), file); const int complete = feof(file) && !ferror(file); fclose(file); size_t digits = length; while (digits && (text[digits - 1] == '\n' || text[digits - 1] == '\r')) --digits; int valid = complete && digits == 64; if (valid) { for (size_t i = 0; i < 32; ++i) { const int hi = hex_digit(text[i * 2]); const int lo = hex_digit(text[i * 2 + 1]); if (hi < 0 || lo < 0) { valid = 0; break; } key[i] = (unsigned char)((hi << 4) | lo); } } clear_bytes(text, sizeof(text)); if (!valid) clear_bytes(key, 32); return valid; } static unsigned char* read_credential(const char* directory, size_t* length) { char path[4096]; const int printed = snprintf(path, sizeof(path), "%s/credential.amwkey", directory); if (printed < 0 || (size_t)printed >= sizeof(path)) return NULL; FILE* file = fopen(path, "rb"); if (!file) return NULL; unsigned char* bytes = (unsigned char*)malloc(1024 * 1024 + 1); if (!bytes) { fclose(file); return NULL; } const size_t size = fread(bytes, 1, 1024 * 1024 + 1, file); const int complete = feof(file) && !ferror(file); fclose(file); if (!complete || size == 0 || size > 1024 * 1024) { clear_bytes(bytes, 1024 * 1024 + 1); free(bytes); return NULL; } *length = size; return bytes; } static int keyed_main(int argc, char** argv) { const int member = !strcmp(argv[1], "--member") || !strcmp(argv[1], "--external-member"); const int external = !strcmp(argv[1], "--external") || !strcmp(argv[1], "--external-member"); if ((!member && !external) || (member && !external && argc != 6) || (external && !member && argc != 7) || (external && member && argc != 8)) return 2; const char* project = argv[2]; const char* runtime = argv[3]; const char* output = argv[4]; const char* private_output = external ? argv[5] : NULL; const char* index_file = external ? argv[6] : NULL; const char* member_file = member ? argv[argc - 1] : NULL; unsigned char member_key[32] = {0}, index_key[32] = {0}; unsigned char* credential = NULL; size_t credential_length = 0; amwc_preview_operation* operation = NULL; char error[256] = {0}; int result = 1; if ((member && !read_key(member_file, member_key)) || (external && !read_key(index_file, index_key))) { fputs("Key file must contain 64 hex digits and an optional newline.\n", stderr); goto done; } uint32_t revision = 0; uint64_t capabilities = 0; const uint64_t required = AMWC_PREVIEW_CAP_BUILD_SITE | AMWC_PREVIEW_CAP_VERIFY_SITE | AMWC_PREVIEW_CAP_OPERATION_CANCEL | AMWC_PREVIEW_CAP_BUILD_PROGRESS | (member ? AMWC_PREVIEW_CAP_MEMBER_CONTENT : 0) | (external ? AMWC_PREVIEW_CAP_EXTERNAL_INDEX : 0); if (amwc_preview_query(&revision, &capabilities) != AMWC_PREVIEW_OK || revision != AMWC_PREVIEW_ABI_REVISION || (capabilities & required) != required) { fputs("Unsupported compiler preview ABI or capabilities.\n", stderr); goto done; } if (amwc_preview_operation_prepare(&operation) != AMWC_PREVIEW_OK) goto done; if (amwc_preview_operation_set_build_progress(operation, on_build_progress, stdout) != AMWC_PREVIEW_OK) goto done; const int32_t built = amwc_preview_build_site(operation, project, (uint64_t)strlen(project), runtime, (uint64_t)strlen(runtime), output, (uint64_t)strlen(output), private_output, private_output ? (uint64_t)strlen(private_output) : 0, member ? member_key : NULL, member ? 32 : 0, external ? index_key : NULL, external ? 32 : 0, error, sizeof(error)); amwc_preview_operation_release(operation); operation = NULL; if (built != AMWC_PREVIEW_OK) { fprintf(stderr, "Build failed (%d): %s\n", built, error); goto done; } if (external) { credential = read_credential(private_output, &credential_length); if (!credential) { fputs("Cannot read generated private credential for verification.\n", stderr); goto done; } } if (amwc_preview_operation_prepare(&operation) != AMWC_PREVIEW_OK) goto done; uint64_t resources = 0; const int32_t verified = amwc_preview_verify_site(operation, output, (uint64_t)strlen(output), credential, (uint64_t)credential_length, member ? member_key : NULL, member ? 32 : 0, &resources, error, sizeof(error)); if (verified != AMWC_PREVIEW_OK || resources == 0) { fprintf(stderr, "Site verification failed (%d): %s\n", verified, error); goto done; } printf("Verified site built with %llu app resources; publication is a separate step.\n", (unsigned long long)resources); result = 0; done: if (operation) amwc_preview_operation_release(operation); if (credential) { clear_bytes(credential, 1024 * 1024 + 1); free(credential); } clear_bytes(member_key, sizeof(member_key)); clear_bytes(index_key, sizeof(index_key)); return result; } int main(int argc, char** argv) { if (argc > 1 && argv[1][0] == '-') return keyed_main(argc, argv); if (argc != 4) { fputs("Usage: native-c-build PROJECT RUNTIME OUTPUT\n" " native-c-build --member PROJECT RUNTIME OUTPUT MEMBER_KEY_FILE\n" " native-c-build --external PROJECT RUNTIME OUTPUT PRIVATE_OUTPUT INDEX_KEY_FILE\n" " native-c-build --external-member PROJECT RUNTIME OUTPUT PRIVATE_OUTPUT INDEX_KEY_FILE MEMBER_KEY_FILE\n", stderr); return 2; } uint32_t revision = 0; uint64_t capabilities = 0; const uint64_t required = AMWC_PREVIEW_CAP_BUILD_SITE | AMWC_PREVIEW_CAP_VERIFY_SITE | AMWC_PREVIEW_CAP_OPERATION_CANCEL | AMWC_PREVIEW_CAP_BUILD_PROGRESS; if (amwc_preview_query(&revision, &capabilities) != AMWC_PREVIEW_OK || revision != AMWC_PREVIEW_ABI_REVISION || (capabilities & required) != required) { fprintf(stderr, "Unsupported compiler preview ABI or capabilities\n"); return 1; } amwc_preview_operation* operation = NULL; if (amwc_preview_operation_prepare(&operation) != AMWC_PREVIEW_OK) { fprintf(stderr, "Cannot prepare compiler operation\n"); return 1; } if (amwc_preview_operation_set_build_progress(operation, on_build_progress, stdout) != AMWC_PREVIEW_OK) { amwc_preview_operation_release(operation); fputs("Cannot configure build progress.\n", stderr); return 1; } char error[256] = {0}; const int32_t status = amwc_preview_build_hosted_site(operation, argv[1], (uint64_t)strlen(argv[1]), argv[2], (uint64_t)strlen(argv[2]), argv[3], (uint64_t)strlen(argv[3]), error, sizeof(error)); amwc_preview_operation_release(operation); if (status != AMWC_PREVIEW_OK) { fprintf(stderr, "Build failed (%d): %s\n", status, error); return 1; } operation = NULL; if (amwc_preview_operation_prepare(&operation) != AMWC_PREVIEW_OK) { fputs("Cannot prepare verification operation\n", stderr); return 1; } uint64_t resources = 0; const int32_t verified = amwc_preview_verify_site(operation, argv[3], (uint64_t)strlen(argv[3]), NULL, 0, NULL, 0, &resources, error, sizeof(error)); amwc_preview_operation_release(operation); if (verified != AMWC_PREVIEW_OK || resources == 0) { fprintf(stderr, "Site verification failed (%d): %s\n", verified, error); return 1; } printf("Verified site built with %llu app resources; publication is a separate step.\n", (unsigned long long)resources); return 0; }