/* * Makefile-Based Static Linking Test * * This test validates static linking configuration by parsing the Makefile * instead of analyzing compiled binaries. This approach is faster, more reliable, * and catches configuration issues at the source. */ #include #include #include #include // Test result tracking static int tests_run = 0; static int tests_passed = 0; // Test macros #define ASSERT(condition, message) \ do { \ tests_run++; \ if (condition) { \ printf("✓ %s: PASSED\n", message); \ tests_passed++; \ } else { \ printf("✗ %s: FAILED\n", message); \ } \ } while(0) #define ASSERT_CONTAINS(haystack, needle, message) \ ASSERT(strstr(haystack, needle) != NULL, message) #define ASSERT_NOT_CONTAINS(haystack, needle, message) \ ASSERT(strstr(haystack, needle) == NULL, message) // File reading utility char* read_file(const char* filename) { FILE* file = fopen(filename, "r"); if (!file) { printf("ERROR: Cannot open %s\n", filename); return NULL; } fseek(file, 0, SEEK_END); long length = ftell(file); fseek(file, 0, SEEK_SET); char* content = malloc(length + 1); if (!content) { fclose(file); return NULL; } fread(content, 1, length, file); content[length] = '\0'; fclose(file); return content; } // Extract variable value from Makefile char* extract_makefile_variable(const char* content, const char* variable) { char search_pattern[256]; snprintf(search_pattern, sizeof(search_pattern), "%s =", variable); char* line_start = strstr(content, search_pattern); if (!line_start) { snprintf(search_pattern, sizeof(search_pattern), "%s=", variable); line_start = strstr(content, search_pattern); } if (!line_start) return NULL; // Find start of value (after '=') char* value_start = strchr(line_start, '='); if (!value_start) return NULL; value_start++; // Skip '=' // Skip whitespace while (*value_start == ' ' || *value_start == '\t') { value_start++; } // Find end of line char* line_end = strchr(value_start, '\n'); if (!line_end) { line_end = value_start + strlen(value_start); } // Handle line continuations with backslash char* result = malloc(2048); char* result_ptr = result; char* current = value_start; while (current < line_end) { if (*current == '\\' && (current + 1) < line_end && *(current + 1) == '\n') { // Line continuation - skip to next line current += 2; // Skip '\\\n' // Find next non-whitespace while (current < line_end && (*current == ' ' || *current == '\t')) { current++; } *result_ptr++ = ' '; // Add space between continued lines } else { *result_ptr++ = *current++; } } *result_ptr = '\0'; // Trim trailing whitespace result_ptr--; while (result_ptr > result && (*result_ptr == ' ' || *result_ptr == '\t')) { *result_ptr-- = '\0'; } return result; } // Test static linking configuration void test_static_linking_flags(const char* makefile_content) { printf("\n=== Static Linking Flags Test ===\n"); // Check TEST_LDFLAGS contains -static char* test_ldflags = extract_makefile_variable(makefile_content, "TEST_LDFLAGS"); if (test_ldflags) { ASSERT_CONTAINS(test_ldflags, "-static", "TEST_LDFLAGS contains -static flag"); free(test_ldflags); } else { ASSERT(0, "TEST_LDFLAGS variable found in Makefile"); } // Check ARM64_TEST_LDFLAGS contains -static char* arm64_test_ldflags = extract_makefile_variable(makefile_content, "ARM64_TEST_LDFLAGS"); if (arm64_test_ldflags) { ASSERT_CONTAINS(arm64_test_ldflags, "-static", "ARM64_TEST_LDFLAGS contains -static flag"); free(arm64_test_ldflags); } else { ASSERT(0, "ARM64_TEST_LDFLAGS variable found in Makefile"); } } // Test forbidden dynamic library links void test_forbidden_dynamic_links(const char* makefile_content) { printf("\n=== Forbidden Dynamic Links Test ===\n"); // List of libraries that should not be dynamically linked in core tests const char* forbidden_libs[] = { "-lsecp256k1", // Should be statically included "-lsodium", // Not used "-lwally", // Not used "-lmbedtls", // Replaced with OpenSSL "-lmbedx509", // Replaced with OpenSSL "-lmbedcrypto", // Replaced with OpenSSL NULL }; for (int i = 0; forbidden_libs[i] != NULL; i++) { // Check that forbidden library is not in TEST_LDFLAGS char* test_ldflags = extract_makefile_variable(makefile_content, "TEST_LDFLAGS"); if (test_ldflags) { char message[256]; snprintf(message, sizeof(message), "TEST_LDFLAGS does not contain %s", forbidden_libs[i]); ASSERT_NOT_CONTAINS(test_ldflags, forbidden_libs[i], message); free(test_ldflags); } } } // Test that we use the static library void test_static_library_usage(const char* makefile_content) { printf("\n=== Static Library Usage Test ===\n"); // Check that TEST_LDFLAGS links against our static library char* test_ldflags = extract_makefile_variable(makefile_content, "TEST_LDFLAGS"); if (test_ldflags) { ASSERT_CONTAINS(test_ldflags, "-lnostr_core", "TEST_LDFLAGS links against libnostr_core"); ASSERT_CONTAINS(test_ldflags, "-lm", "TEST_LDFLAGS links against math library"); free(test_ldflags); } // Check ARM64 version char* arm64_test_ldflags = extract_makefile_variable(makefile_content, "ARM64_TEST_LDFLAGS"); if (arm64_test_ldflags) { ASSERT_CONTAINS(arm64_test_ldflags, "-lnostr_core_arm64", "ARM64_TEST_LDFLAGS links against ARM64 static library"); free(arm64_test_ldflags); } } // Test that compilation flags disable problematic features for static tests only void test_compilation_flags(const char* makefile_content) { printf("\n=== Compilation Flags Test ===\n"); // Check TEST_CFLAGS contains DISABLE_NIP05 (static tests need to avoid curl) char* test_cflags = extract_makefile_variable(makefile_content, "TEST_CFLAGS"); if (test_cflags) { ASSERT_CONTAINS(test_cflags, "-DDISABLE_NIP05", "TEST_CFLAGS disables NIP-05 to avoid curl dependency"); free(test_cflags); } // Check that main compilation does NOT disable NIP05 (NIP-05 should be enabled by default) // Look for DISABLE_NIP05 in object file compilation rules - should NOT be there char* obj_rule_start = strstr(makefile_content, "%.o: %.c"); if (obj_rule_start) { char* obj_rule_end = strstr(obj_rule_start, "\n\n"); if (!obj_rule_end) obj_rule_end = makefile_content + strlen(makefile_content); char* obj_rule = malloc(obj_rule_end - obj_rule_start + 1); strncpy(obj_rule, obj_rule_start, obj_rule_end - obj_rule_start); obj_rule[obj_rule_end - obj_rule_start] = '\0'; ASSERT_NOT_CONTAINS(obj_rule, "-DDISABLE_NIP05", "Main compilation does not disable NIP-05"); free(obj_rule); } } // Test static curl usage (no dynamic -lcurl) void test_static_curl_usage(const char* makefile_content) { printf("\n=== Static Curl Usage Test ===\n"); // Count occurrences of -lcurl (should be zero - we use static libcurl.a) int curl_count = 0; const char* search_pos = makefile_content; while ((search_pos = strstr(search_pos, "-lcurl")) != NULL) { curl_count++; search_pos += 6; // Move past "-lcurl" } char message[256]; snprintf(message, sizeof(message), "No dynamic curl usage found (found %d -lcurl occurrences)", curl_count); ASSERT(curl_count == 0, message); // Verify HTTP and NIP-05 tests use static libcurl.a instead ASSERT_CONTAINS(makefile_content, "./curl-install/lib/libcurl.a", "Static libcurl.a is used"); // Verify curl include path is used ASSERT_CONTAINS(makefile_content, "-I./curl-install/include", "Curl include path is used"); // Verify both HTTP and NIP-05 tests use static linking char* http_test_line = strstr(makefile_content, "$(HTTP_TEST_EXEC): tests/http_test.c"); if (http_test_line) { char* next_rule = strstr(http_test_line, "\n\n"); if (!next_rule) next_rule = makefile_content + strlen(makefile_content); char* http_section = malloc(next_rule - http_test_line + 1); strncpy(http_section, http_test_line, next_rule - http_test_line); http_section[next_rule - http_test_line] = '\0'; ASSERT_CONTAINS(http_section, "./curl-install/lib/libcurl.a", "HTTP test uses static libcurl.a"); ASSERT_CONTAINS(http_section, "-static", "HTTP test uses static linking"); free(http_section); } char* nip05_test_line = strstr(makefile_content, "$(NIP05_TEST_EXEC): tests/nip05_test.c"); if (nip05_test_line) { char* next_rule = strstr(nip05_test_line, "\n\n"); if (!next_rule) next_rule = makefile_content + strlen(makefile_content); char* nip05_section = malloc(next_rule - nip05_test_line + 1); strncpy(nip05_section, nip05_test_line, next_rule - nip05_test_line); nip05_section[next_rule - nip05_test_line] = '\0'; ASSERT_CONTAINS(nip05_section, "./curl-install/lib/libcurl.a", "NIP-05 test uses static libcurl.a"); ASSERT_CONTAINS(nip05_section, "-static", "NIP-05 test uses static linking"); free(nip05_section); } } // Test that only one Makefile exists void test_single_makefile_policy() { printf("\n=== Single Makefile Policy Test ===\n"); // Check that subdirectory Makefiles don't exist or are minimal/deprecated int makefile_violations = 0; // Check tests/Makefile if (access("tests/Makefile", F_OK) == 0) { char* tests_makefile = read_file("tests/Makefile"); if (tests_makefile) { // If tests/Makefile exists and contains actual build rules, it's a violation if (strstr(tests_makefile, "LDFLAGS") || strstr(tests_makefile, "gcc")) { makefile_violations++; printf("WARNING: tests/Makefile contains build rules (should be consolidated)\n"); } free(tests_makefile); } } // Check nostr_websocket/Makefile if (access("nostr_websocket/Makefile", F_OK) == 0) { char* websocket_makefile = read_file("nostr_websocket/Makefile"); if (websocket_makefile) { // If websocket Makefile exists and contains build rules, it's a violation if (strstr(websocket_makefile, "LDFLAGS") || strstr(websocket_makefile, "gcc")) { makefile_violations++; printf("WARNING: nostr_websocket/Makefile contains build rules (should be consolidated)\n"); } free(websocket_makefile); } } char message[256]; snprintf(message, sizeof(message), "No Makefile policy violations found (found %d violations)", makefile_violations); ASSERT(makefile_violations == 0, message); } int main() { printf("Makefile-Based Static Linking Test\n"); printf("==================================\n"); printf("Testing static linking configuration by parsing Makefile...\n"); // Read the main Makefile char* makefile_content = read_file("Makefile"); if (!makefile_content) { printf("FATAL: Cannot read Makefile\n"); return 1; } // Run all tests test_static_linking_flags(makefile_content); test_forbidden_dynamic_links(makefile_content); test_static_library_usage(makefile_content); test_compilation_flags(makefile_content); test_static_curl_usage(makefile_content); test_single_makefile_policy(); free(makefile_content); // Summary printf("\n============================================\n"); printf("TEST SUMMARY\n"); printf("============================================\n"); printf("Tests run: %d\n", tests_run); printf("Tests passed: %d\n", tests_passed); if (tests_passed == tests_run) { printf("ALL TESTS PASSED!\n"); printf("✅ Makefile static linking configuration is correct\n"); printf("✅ No forbidden dynamic dependencies\n"); printf("✅ Single Makefile policy enforced\n"); return 0; } else { printf("%d TESTS FAILED!\n", tests_run - tests_passed); printf("❌ Makefile configuration needs fixes\n"); return 1; } }