diff --git a/CMakeLists.txt b/CMakeLists.txt index ad75911..ee177bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,10 @@ project(dpm) set(CMAKE_CXX_STANDARD 20) -# Create modules directory -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +# Set binary output directories +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/modules) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # Main DPM executable add_executable( @@ -28,10 +30,13 @@ target_link_libraries(dpm dl) target_link_options(dpm PRIVATE -rdynamic) # Add the info module by including its CMakeLists.txt -add_subdirectory(modules/info) +add_subdirectory(modules/info ${CMAKE_BINARY_DIR}/build-modules/info) + +# add the build module by including that +add_subdirectory(modules/build ${CMAKE_BINARY_DIR}/build-modules/build) # Create a custom target for building all modules -add_custom_target(modules DEPENDS info) +add_custom_target(modules DEPENDS info build) # Installation rules install(TARGETS dpm DESTINATION bin) @@ -45,7 +50,7 @@ install( # Install modules install( - DIRECTORY ${CMAKE_BINARY_DIR}/modules/ + DIRECTORY ${CMAKE_BINARY_DIR}/bin/modules/ DESTINATION /usr/lib/dpm/modules FILES_MATCHING PATTERN "*.so" ) \ No newline at end of file diff --git a/dpmdk/include/CommonModuleAPI.hpp b/dpmdk/include/CommonModuleAPI.hpp index 9de44b9..944effc 100644 --- a/dpmdk/include/CommonModuleAPI.hpp +++ b/dpmdk/include/CommonModuleAPI.hpp @@ -29,6 +29,9 @@ #pragma once +#include +#include + /** * @brief Fatal log level constant * @@ -134,4 +137,53 @@ extern "C" { * * Defines the version string for the DPM core system. */ -#define DPM_VERSION "0.1.0" \ No newline at end of file +#define DPM_VERSION "0.1.0" + +// If we're building in standalone mode, add support for standalone execution +#ifdef BUILD_STANDALONE + +// Declare, but don't define the standalone implementations +// These will be defined in the main file through the DPM_STANDALONE_IMPL macro + +/** + * @brief Helper macro to create a standalone main function for modules + * + * This macro defines a main() function that initializes the module and + * processes command-line arguments to execute commands directly. + */ +#define DPM_MODULE_STANDALONE_MAIN() \ +extern "C" void dpm_log(int level, const char* message) { \ + const char* level_str; \ + switch (level) { \ + case 0: level_str = "FATAL"; break; \ + case 1: level_str = "ERROR"; break; \ + case 2: level_str = "WARN"; break; \ + case 3: level_str = "INFO"; break; \ + case 4: level_str = "DEBUG"; break; \ + default: level_str = "UNKNOWN"; break; \ + } \ + std::cout << "[" << level_str << "] " << message << std::endl; \ +} \ +extern "C" const char* dpm_get_config(const char* section, const char* key) { \ + return nullptr; \ +} \ +int main(int argc, char** argv) { \ + std::cout << "Module version: " << dpm_module_get_version() << std::endl; \ + std::cout << "Description: " << dpm_get_description() << std::endl; \ + \ + /* Default to "help" if no command is provided */ \ + const char* command = "help"; \ + \ + /* If arguments are provided, use the first as command */ \ + if (argc > 1) { \ + command = argv[1]; \ + /* Shift the argument array for the command handler */ \ + argv++; \ + argc--; \ + } \ + \ + std::cout << "Executing command: " << command << std::endl; \ + return dpm_module_execute(command, argc - 1, argv + 1); \ +} + +#endif // BUILD_STANDALONE \ No newline at end of file diff --git a/dpmdk/src/CommonModuleAPI.cpp b/dpmdk/src/CommonModuleAPI.cpp index e48894a..f6b5418 100644 --- a/dpmdk/src/CommonModuleAPI.cpp +++ b/dpmdk/src/CommonModuleAPI.cpp @@ -29,9 +29,4 @@ * mailing list at: https://lists.darkhorselinux.org/mailman/listinfo/dhlp-contributors */ -#include "module.hpp" - -/** - * @note The implementation of dpm_get_config and dpm_log are provided by the DPM core. - * Each module must implement dpm_module_execute, dpm_module_get_version, and dpm_get_description. - */ \ No newline at end of file +#include "CommonModuleAPI.hpp" diff --git a/modules/build/CMakeLists.txt b/modules/build/CMakeLists.txt new file mode 100644 index 0000000..8729268 --- /dev/null +++ b/modules/build/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 3.22) +project(build_module) + +set(CMAKE_CXX_STANDARD 20) + +# Set DPM_ROOT_DIR based on whether this is a standalone build or part of the main build +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(DPM_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../..") +else() + set(DPM_ROOT_DIR "${CMAKE_SOURCE_DIR}") +endif() + +# Module version - used by DPM +add_library(build MODULE + build.cpp + src/buildFuncs.cpp +) + +# Set output properties +set_target_properties( + build PROPERTIES + PREFIX "" + SUFFIX ".so" +) + +# Include directories +target_include_directories(build PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${DPM_ROOT_DIR} +) + +# Link with filesystem library +target_link_libraries(build stdc++fs) + +# Standalone version - used for debugging +add_executable(build_standalone + build.cpp + src/buildFuncs.cpp +) + +# Define the BUILD_STANDALONE macro for the standalone build +target_compile_definitions(build_standalone PRIVATE BUILD_STANDALONE) + +# Include directories for standalone +target_include_directories(build_standalone PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${DPM_ROOT_DIR} +) + +# Link with filesystem library for standalone +target_link_libraries(build_standalone stdc++fs) + +# Set the output name for the standalone executable +set_target_properties( + build_standalone PROPERTIES + OUTPUT_NAME "build_debug" +) \ No newline at end of file diff --git a/modules/build/build.cpp b/modules/build/build.cpp new file mode 100644 index 0000000..59d2a3f --- /dev/null +++ b/modules/build/build.cpp @@ -0,0 +1,102 @@ +/** + * @file build.cpp + * @brief DPM build module implementation + * + * Implements a DPM module that creates DPM packages according to specification. + * This module handles the package creation process. + * + * @copyright Copyright (c) 2025 SILO GROUP LLC + * @author Chris Punches + * + * Part of the Dark Horse Linux Package Manager (DPM) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * For bug reports or contributions, please contact the dhlp-contributors + * mailing list at: https://lists.darkhorselinux.org/mailman/listinfo/dhlp-contributors + */ + +#include +#include +#include +#include + +#include "include/buildFuncs.hpp" + +/** + * @def MODULE_VERSION + * @brief Version information for the build module + * + * Defines the version string that will be returned by dpm_module_get_version() + */ +#define MODULE_VERSION "0.1.0" + +/** + * @brief Returns the module version string + * + * Required implementation of the DPM module interface that provides + * version information for the build module. + * + * @return Const char pointer to the module version string + */ +extern "C" const char* dpm_module_get_version(void) { + return MODULE_VERSION; +} + +/** + * @brief Returns the module description string + * + * Required implementation of the DPM module interface that provides + * a human-readable description of the build module and its functionality. + * + * @return Const char pointer to the module description string + */ +extern "C" const char* dpm_get_description(void) { + return "DPM Build Module - Creates DPM packages according to specification"; +} + +/** + * @brief Main entry point for the build module + * + * Required implementation of the DPM module interface that serves as the + * primary execution point for the module. Parses the command and routes + * execution to the appropriate handler function. + * + * @param command The command string to execute + * @param argc Number of arguments + * @param argv Array of argument strings + * @return 0 on success, non-zero on failure + */ +extern "C" int dpm_module_execute(const char* command, int argc, char** argv) { + // Parse the command + Command cmd = parse_command(command); + + // Route to the appropriate command handler + switch (cmd) { + case CMD_CREATE: + return cmd_create(argc, argv); + + case CMD_HELP: + return cmd_help(argc, argv); + + case CMD_UNKNOWN: + default: + return cmd_unknown(command, argc, argv); + } +} + +// If we're building in standalone mode, include the main function +#ifdef BUILD_STANDALONE +DPM_MODULE_STANDALONE_MAIN() +#endif // BUILD_STANDALONE \ No newline at end of file diff --git a/modules/build/include/buildFuncs.hpp b/modules/build/include/buildFuncs.hpp new file mode 100644 index 0000000..f277f5a --- /dev/null +++ b/modules/build/include/buildFuncs.hpp @@ -0,0 +1,122 @@ +/** + * @file buildFuncs.hpp + * @brief Header file for the build module support functions + * + * Defines functions and enumerations for the build module which creates + * DPM packages according to specification. + * + * @copyright Copyright (c) 2025 SILO GROUP LLC + * @author Chris Punches + * + * Part of the Dark Horse Linux Package Manager (DPM) + */ +#pragma once + +#include +#include +#include +#include +#include + +/** + * @enum Command + * @brief Enumeration of supported commands for the build module + */ +enum Command { + CMD_UNKNOWN, /**< Unknown or unsupported command */ + CMD_HELP, /**< Display help information */ + CMD_CREATE /**< Create a new DPM package */ +}; + +/** + * @struct BuildOptions + * @brief Structure to store parsed command-line options for the build module + */ +struct BuildOptions { + std::string output_dir; /**< Directory where to save the built package */ + std::string contents_dir; /**< Directory with package contents */ + std::string metadata_dir; /**< Directory with package metadata */ + std::string hooks_dir; /**< Directory with package hooks */ + std::string package_name; /**< Name of the package to build */ + std::string signature_key; /**< Path to the GPG key for signing the package */ + bool force; /**< Flag to force package creation even if warnings occur */ + bool verbose; /**< Flag for verbose output */ + + // Constructor with default values + BuildOptions() : + output_dir("."), + contents_dir(""), + metadata_dir(""), + hooks_dir(""), + package_name(""), + signature_key(""), + force(false), + verbose(false) {} +}; + +/** + * @brief Handler for the help command + * + * Displays information about available commands in the build module. + * + * @param argc Number of arguments + * @param argv Array of arguments + * @return 0 on success, non-zero on failure + */ +int cmd_help(int argc, char** argv); + +/** + * @brief Handler for the create command + * + * Processes arguments and creates a DPM package. + * + * @param argc Number of arguments + * @param argv Array of arguments + * @return 0 on success, non-zero on failure + */ +int cmd_create(int argc, char** argv); + +/** + * @brief Handler for unknown commands + * + * Displays an error message for unrecognized commands. + * + * @param command The unrecognized command string + * @param argc Number of arguments + * @param argv Array of arguments + * @return 1 to indicate failure + */ +int cmd_unknown(const char* command, int argc, char** argv); + +/** + * @brief Parses a command string into a Command enum value + * + * Converts a command string to the appropriate Command enum value + * for internal routing. + * + * @param cmd_str The command string to parse + * @return The corresponding Command enum value + */ +Command parse_command(const char* cmd_str); + +/** + * @brief Parses command-line arguments for the create command + * + * Processes command-line arguments and populates a BuildOptions structure. + * + * @param argc Number of arguments + * @param argv Array of arguments + * @param options Reference to BuildOptions structure to populate + * @return 0 on success, non-zero on failure + */ +int parse_create_options(int argc, char** argv, BuildOptions& options); + +/** + * @brief Validates the build options + * + * Ensures that all required options are provided and valid. + * + * @param options The BuildOptions structure to validate + * @return 0 if options are valid, non-zero otherwise + */ +int validate_build_options(const BuildOptions& options); \ No newline at end of file diff --git a/modules/build/src/buildFuncs.cpp b/modules/build/src/buildFuncs.cpp new file mode 100644 index 0000000..6cdda20 --- /dev/null +++ b/modules/build/src/buildFuncs.cpp @@ -0,0 +1,234 @@ +/** + * @file buildFuncs.cpp + * @brief Implementation of the build module support functions + * + * Implements functions for the build module that create DPM packages + * according to the specification. + * + * @copyright Copyright (c) 2025 SILO GROUP LLC + * @author Chris Punches + * + * Part of the Dark Horse Linux Package Manager (DPM) + */ + +#include "buildFuncs.hpp" +#include +#include + +/** + * @brief Handler for the help command + */ +int cmd_help(int argc, char** argv) { + dpm_log(LOG_INFO, "DPM Build Module - Creates DPM packages according to specification"); + dpm_log(LOG_INFO, "Available commands:"); + dpm_log(LOG_INFO, " create - Create a new DPM package"); + dpm_log(LOG_INFO, " help - Display this help message"); + dpm_log(LOG_INFO, ""); + dpm_log(LOG_INFO, "Usage: dpm build create [options]"); + dpm_log(LOG_INFO, "Options:"); + dpm_log(LOG_INFO, " -o, --output-dir DIR Directory to save the built package (default: current directory)"); + dpm_log(LOG_INFO, " -c, --contents DIR Directory with package contents (required)"); + dpm_log(LOG_INFO, " -m, --metadata DIR Directory with package metadata (required)"); + dpm_log(LOG_INFO, " -H, --hooks DIR Directory with package hooks (optional)"); + dpm_log(LOG_INFO, " -n, --name NAME Package name (required if not in metadata)"); + dpm_log(LOG_INFO, " -s, --sign KEY Path to GPG key for signing the package (optional)"); + dpm_log(LOG_INFO, " -f, --force Force package creation even if warnings occur"); + dpm_log(LOG_INFO, " -v, --verbose Enable verbose output"); + dpm_log(LOG_INFO, " -h, --help Display this help message"); + return 0; +} + +/** + * @brief Handler for unknown commands + */ +int cmd_unknown(const char* command, int argc, char** argv) { + std::string msg = "Unknown command: "; + msg += (command ? command : ""); + dpm_log(LOG_WARN, msg.c_str()); + dpm_log(LOG_WARN, "Run 'dpm build help' for a list of available commands"); + return 1; +} + +/** + * @brief Parses a command string to Command enum + */ +Command parse_command(const char* cmd_str) { + if (cmd_str == nullptr || strlen(cmd_str) == 0) { + return CMD_HELP; + } + + if (strcmp(cmd_str, "help") == 0) { + return CMD_HELP; + } + else if (strcmp(cmd_str, "create") == 0) { + return CMD_CREATE; + } + + return CMD_UNKNOWN; +} + +/** + * @brief Handler for the create command + */ +int cmd_create(int argc, char** argv) { + BuildOptions options; + + // Parse command-line options + int parse_result = parse_create_options(argc, argv, options); + if (parse_result != 0) { + return parse_result; + } + + // Validate options + int validate_result = validate_build_options(options); + if (validate_result != 0) { + return validate_result; + } + + // Log the operation + if (options.verbose) { + dpm_log(LOG_INFO, "Creating DPM package with the following options:"); + dpm_log(LOG_INFO, (" Output directory: " + options.output_dir).c_str()); + dpm_log(LOG_INFO, (" Contents directory: " + options.contents_dir).c_str()); + dpm_log(LOG_INFO, (" Metadata directory: " + options.metadata_dir).c_str()); + + if (!options.hooks_dir.empty()) { + dpm_log(LOG_INFO, (" Hooks directory: " + options.hooks_dir).c_str()); + } + + if (!options.package_name.empty()) { + dpm_log(LOG_INFO, (" Package name: " + options.package_name).c_str()); + } + + if (!options.signature_key.empty()) { + dpm_log(LOG_INFO, (" Signature key: " + options.signature_key).c_str()); + } + + if (options.force) { + dpm_log(LOG_INFO, " Force: Yes"); + } + } + + // For now, just log that we would create the package + dpm_log(LOG_INFO, "Package creation functionality not yet implemented"); + dpm_log(LOG_INFO, "Would create package using the provided options"); + + return 0; +} + +/** + * @brief Parses command-line arguments for the create command + */ +int parse_create_options(int argc, char** argv, BuildOptions& options) { + static struct option long_options[] = { + {"output-dir", required_argument, 0, 'o'}, + {"contents", required_argument, 0, 'c'}, + {"metadata", required_argument, 0, 'm'}, + {"hooks", required_argument, 0, 'H'}, + {"name", required_argument, 0, 'n'}, + {"sign", required_argument, 0, 's'}, + {"force", no_argument, 0, 'f'}, + {"verbose", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + + // Skip program name and module name + int adjusted_argc = argc; + char** adjusted_argv = argv; + + // Reset getopt + optind = 0; + opterr = 1; + + int opt; + int option_index = 0; + + while ((opt = getopt_long(adjusted_argc, adjusted_argv, "o:c:m:H:n:s:fvh", long_options, &option_index)) != -1) { + switch (opt) { + case 'o': + options.output_dir = optarg; + break; + case 'c': + options.contents_dir = optarg; + break; + case 'm': + options.metadata_dir = optarg; + break; + case 'H': + options.hooks_dir = optarg; + break; + case 'n': + options.package_name = optarg; + break; + case 's': + options.signature_key = optarg; + break; + case 'f': + options.force = true; + break; + case 'v': + options.verbose = true; + break; + case 'h': + cmd_help(0, nullptr); + return 1; + case '?': + // Error message is printed by getopt + dpm_log(LOG_ERROR, "Run 'dpm build create --help' for usage information"); + return 1; + default: + dpm_log(LOG_ERROR, "Unknown option"); + return 1; + } + } + + return 0; +} + +/** + * @brief Validates the build options + */ +int validate_build_options(const BuildOptions& options) { + // Check if contents directory is provided and exists + if (options.contents_dir.empty()) { + dpm_log(LOG_ERROR, "Contents directory is required (--contents)"); + return 1; + } + + if (!std::filesystem::exists(options.contents_dir)) { + dpm_log(LOG_ERROR, ("Contents directory does not exist: " + options.contents_dir).c_str()); + return 1; + } + + // Check if metadata directory is provided and exists + if (options.metadata_dir.empty()) { + dpm_log(LOG_ERROR, "Metadata directory is required (--metadata)"); + return 1; + } + + if (!std::filesystem::exists(options.metadata_dir)) { + dpm_log(LOG_ERROR, ("Metadata directory does not exist: " + options.metadata_dir).c_str()); + return 1; + } + + // Check if hooks directory exists if provided + if (!options.hooks_dir.empty() && !std::filesystem::exists(options.hooks_dir)) { + dpm_log(LOG_ERROR, ("Hooks directory does not exist: " + options.hooks_dir).c_str()); + return 1; + } + + // Check if output directory exists + if (!std::filesystem::exists(options.output_dir)) { + dpm_log(LOG_ERROR, ("Output directory does not exist: " + options.output_dir).c_str()); + return 1; + } + + // Check if signature key exists if provided + if (!options.signature_key.empty() && !std::filesystem::exists(options.signature_key)) { + dpm_log(LOG_ERROR, ("Signature key file does not exist: " + options.signature_key).c_str()); + return 1; + } + + return 0; +} diff --git a/modules/info/CMakeLists.txt b/modules/info/CMakeLists.txt index bb9d0fa..30b92cb 100644 --- a/modules/info/CMakeLists.txt +++ b/modules/info/CMakeLists.txt @@ -3,18 +3,13 @@ project(info_module) set(CMAKE_CXX_STANDARD 20) -# Set output directory for standalone builds +# Set DPM_ROOT_DIR based on whether this is a standalone build or part of the main build if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - set(MODULE_OUTPUT_DIR "${CMAKE_BINARY_DIR}/modules") set(DPM_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../..") else() - set(MODULE_OUTPUT_DIR "${CMAKE_BINARY_DIR}/modules") set(DPM_ROOT_DIR "${CMAKE_SOURCE_DIR}") endif() -# Create output directory -file(MAKE_DIRECTORY ${MODULE_OUTPUT_DIR}) - # Create shared library add_library(info MODULE info.cpp @@ -26,11 +21,31 @@ set_target_properties( info PROPERTIES PREFIX "" SUFFIX ".so" - LIBRARY_OUTPUT_DIRECTORY "${MODULE_OUTPUT_DIR}" ) # Include directories target_include_directories(info PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${DPM_ROOT_DIR} +) + +# Standalone version - used for debugging +add_executable(info_standalone + info.cpp + src/infoFuncs.cpp +) + +# Define the BUILD_STANDALONE macro for the standalone build +target_compile_definitions(info_standalone PRIVATE BUILD_STANDALONE) + +# Include directories for standalone +target_include_directories(info_standalone PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${DPM_ROOT_DIR} +) + +# Set the output name for the standalone executable +set_target_properties( + info_standalone PROPERTIES + OUTPUT_NAME "info_debug" ) \ No newline at end of file diff --git a/modules/info/info.cpp b/modules/info/info.cpp index c1bc514..03b4e5c 100644 --- a/modules/info/info.cpp +++ b/modules/info/info.cpp @@ -108,3 +108,8 @@ extern "C" int dpm_module_execute(const char* command, int argc, char** argv) { return cmd_unknown(command, argc, argv); } } + +// If we're building in standalone mode, include the main function +#ifdef BUILD_STANDALONE +DPM_MODULE_STANDALONE_MAIN() +#endif // BUILD_STANDALONE \ No newline at end of file