Compare commits

...

2 Commits

Author SHA1 Message Date
Chris Punches 4588691faf fleshing out stage creation 2025-03-15 05:12:24 -04:00
Chris Punches b034a3679d fleshing out stage creation 2025-03-15 05:12:18 -04:00
6 changed files with 319 additions and 14 deletions

View File

@ -16,6 +16,7 @@ add_library(build MODULE
src/helpers.cpp
src/cli_parsers.cpp
src/commands.cpp
src/package_staging.cpp
)
# Set output properties
@ -40,6 +41,7 @@ add_executable(build_standalone
src/helpers.cpp
src/cli_parsers.cpp
src/commands.cpp
src/package_staging.cpp
)
# Define the BUILD_STANDALONE macro for the standalone build

View File

@ -3,6 +3,7 @@
#include "cli_parsers.hpp"
#include <dpmdk/include/CommonModuleAPI.hpp>
#include <filesystem>
#include "package_staging.hpp"
/**
* @brief Handler for the stage command

View File

@ -0,0 +1,45 @@
/**
* @file package_staging.hpp
* @brief Functions for staging DPM packages
*
* Defines functions for creating and manipulating DPM package staging structures.
*
* @copyright Copyright (c) 2025 SILO GROUP LLC
* @author Chris Punches <chris.punches@silogroup.org>
*
* Part of the Dark Horse Linux Package Manager (DPM)
*/
#pragma once
#include <filesystem>
#include <vector>
#include <string>
#include <fstream>
#include <sys/stat.h>
#include <dpmdk/include/CommonModuleAPI.hpp>
/**
* @brief Stages a DPM package
*
* Creates a package staging directory with the provided parameters.
*
* @param output_dir Directory to save the staged package
* @param contents_dir Directory with package contents
* @param hooks_dir Directory with package hooks (optional)
* @param package_name Package name
* @param package_version Package version
* @param architecture Package architecture
* @param os Package OS (optional)
* @param force Force package staging even if warnings occur
* @return 0 on success, non-zero on failure
*/
int build_package_stage(
const std::string& output_dir,
const std::string& contents_dir,
const std::string& hooks_dir,
const std::string& package_name,
const std::string& package_version,
const std::string& architecture,
const std::string& os,
bool force
);

View File

@ -41,7 +41,7 @@ int parse_create_options(int argc, char** argv, BuildOptions& options) {
std::string option = arg.substr(0, equals_pos);
std::string value = arg.substr(equals_pos + 1);
if (option == "--output-dir") {
if (option == "--output") {
options.output_dir = value;
output_dir_provided = true;
} else if (option == "--contents") {
@ -82,7 +82,7 @@ int parse_create_options(int argc, char** argv, BuildOptions& options) {
}
static struct option long_options[] = {
{"output-dir", required_argument, 0, 'o'},
{"output", required_argument, 0, 'o'},
{"contents", required_argument, 0, 'c'},
{"hooks", required_argument, 0, 'H'},
{"name", required_argument, 0, 'n'},
@ -254,6 +254,18 @@ int validate_build_options(const BuildOptions& options) {
return 0;
}
// Check if output directory is provided
if (options.output_dir.empty()) {
dpm_log(LOG_ERROR, "Output directory is required (--output)");
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 contents directory is provided and exists
if (options.contents_dir.empty()) {
dpm_log(LOG_ERROR, "Contents directory is required (--contents)");
@ -289,11 +301,5 @@ int validate_build_options(const BuildOptions& options) {
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;
}
return 0;
}

View File

@ -1,5 +1,6 @@
#include "commands.hpp"
int cmd_stage(int argc, char** argv) {
// Announce that the stage step is being executed (debug level)
dpm_log(LOG_DEBUG, "Executing stage command");
@ -62,11 +63,17 @@ int cmd_stage(int argc, char** argv) {
dpm_log(LOG_DEBUG, " Force: Yes");
}
// Standard info logs that are always visible
dpm_log(LOG_INFO, "Package staging functionality not yet implemented");
dpm_log(LOG_INFO, "Would stage package directory using the provided options");
return 0;
// Call the build_package_stage function with individual parameters
return build_package_stage(
options.output_dir,
options.contents_dir,
options.hooks_dir,
options.package_name,
options.package_version,
options.architecture,
options.os,
options.force
);
}
int cmd_help(int argc, char** argv) {
@ -91,11 +98,12 @@ int cmd_unknown(const char* command, int argc, char** argv) {
return 1;
}
int cmd_stage_help(int argc, char** argv) {
dpm_log(LOG_INFO, "Usage: dpm build stage [options]");
dpm_log(LOG_INFO, "");
dpm_log(LOG_INFO, "Options:");
dpm_log(LOG_INFO, " -o, --output-dir DIR Directory to save the staged package (required)");
dpm_log(LOG_INFO, " -o, --output DIR Directory to save the staged package (required)");
dpm_log(LOG_INFO, " -c, --contents DIR Directory with package contents (required)");
dpm_log(LOG_INFO, " -H, --hooks DIR Directory with package hooks (optional)");
dpm_log(LOG_INFO, " -n, --name NAME Package name (required)");

View File

@ -0,0 +1,243 @@
/**
* @file package_staging.cpp
* @brief Implementation of DPM package staging functions
*
* Implements functions for staging DPM packages according to specification.
*
* @copyright Copyright (c) 2025 SILO GROUP LLC
* @author Chris Punches <chris.punches@silogroup.org>
*
* Part of the Dark Horse Linux Package Manager (DPM)
*/
#include "package_staging.hpp"
int build_package_stage(
const std::string& output_dir,
const std::string& contents_dir,
const std::string& hooks_dir,
const std::string& package_name,
const std::string& package_version,
const std::string& architecture,
const std::string& os,
bool force
) {
// Log start of package staging
dpm_log(LOG_INFO, "Starting package staging...");
// Create the basic package structure in the output directory
std::string package_dirname = package_name + "-" + package_version + "." + os + "." + architecture;
std::filesystem::path package_dir = std::filesystem::path(output_dir) / package_dirname;
// Check if the package directory already exists
if (std::filesystem::exists(package_dir)) {
if (!force) {
dpm_log(LOG_ERROR, ("Package directory already exists: " + package_dir.string() +
". Use --force to overwrite.").c_str());
return 1;
}
// If force flag is set, try to remove the existing directory
dpm_log(LOG_WARN, ("Removing existing package directory: " + package_dir.string()).c_str());
try {
std::filesystem::remove_all(package_dir);
} catch (const std::filesystem::filesystem_error& e) {
dpm_log(LOG_ERROR, ("Failed to remove existing directory: " + std::string(e.what())).c_str());
return 1;
}
}
// Create the package directory structure
try {
// Create main package directory
std::filesystem::create_directory(package_dir);
// Create subdirectories
std::filesystem::create_directory(package_dir / "contents");
std::filesystem::create_directory(package_dir / "hooks");
std::filesystem::create_directory(package_dir / "metadata");
std::filesystem::create_directory(package_dir / "signatures");
dpm_log(LOG_INFO, ("Created package directory structure at: " + package_dir.string()).c_str());
} catch (const std::filesystem::filesystem_error& e) {
dpm_log(LOG_ERROR, ("Failed to create package directory structure: " + std::string(e.what())).c_str());
return 1;
}
// Copy contents from source directory to package contents directory
try {
std::filesystem::path contents_source = std::filesystem::path(contents_dir);
std::filesystem::path contents_dest = package_dir / "contents";
dpm_log(LOG_INFO, ("Copying contents from: " + contents_source.string() +
" to: " + contents_dest.string()).c_str());
// If the contents source is a directory, copy its contents
if (std::filesystem::is_directory(contents_source)) {
for (const auto& entry : std::filesystem::directory_iterator(contents_source)) {
// Get the relative path from the source directory
std::filesystem::path relative = entry.path().lexically_relative(contents_source);
std::filesystem::path dest_path = contents_dest / relative;
if (entry.is_directory()) {
std::filesystem::create_directories(dest_path);
// Copy the directory contents recursively
for (const auto& subentry : std::filesystem::recursive_directory_iterator(entry)) {
std::filesystem::path subrelative = subentry.path().lexically_relative(contents_source);
std::filesystem::path subdest_path = contents_dest / subrelative;
if (subentry.is_directory()) {
std::filesystem::create_directories(subdest_path);
} else {
std::filesystem::copy_file(subentry.path(), subdest_path,
std::filesystem::copy_options::overwrite_existing);
}
}
} else {
std::filesystem::copy_file(entry.path(), dest_path,
std::filesystem::copy_options::overwrite_existing);
}
}
}
// If the contents source is a file (like a tarball), just copy it
else if (std::filesystem::is_regular_file(contents_source)) {
std::filesystem::path dest_path = contents_dest / contents_source.filename();
std::filesystem::copy_file(contents_source, dest_path,
std::filesystem::copy_options::overwrite_existing);
}
} catch (const std::filesystem::filesystem_error& e) {
dpm_log(LOG_ERROR, ("Failed to copy contents: " + std::string(e.what())).c_str());
return 1;
}
// Copy hooks if provided
if (!hooks_dir.empty()) {
try {
std::filesystem::path hooks_source = std::filesystem::path(hooks_dir);
std::filesystem::path hooks_dest = package_dir / "hooks";
dpm_log(LOG_INFO, ("Copying hooks from: " + hooks_source.string() +
" to: " + hooks_dest.string()).c_str());
// If hooks source is a directory, copy its contents
if (std::filesystem::is_directory(hooks_source)) {
for (const auto& entry : std::filesystem::directory_iterator(hooks_source)) {
// Get the relative path from the source directory
std::filesystem::path relative = entry.path().lexically_relative(hooks_source);
std::filesystem::path dest_path = hooks_dest / relative;
if (entry.is_directory()) {
std::filesystem::create_directories(dest_path);
} else {
std::filesystem::copy_file(entry.path(), dest_path,
std::filesystem::copy_options::overwrite_existing);
// Make hook files executable
chmod(dest_path.c_str(), 0755);
}
}
}
// If hooks source is a file, just copy it
else if (std::filesystem::is_regular_file(hooks_source)) {
std::filesystem::path dest_path = hooks_dest / hooks_source.filename();
std::filesystem::copy_file(hooks_source, dest_path,
std::filesystem::copy_options::overwrite_existing);
// Make hook file executable
chmod(dest_path.c_str(), 0755);
}
} catch (const std::filesystem::filesystem_error& e) {
dpm_log(LOG_ERROR, ("Failed to copy hooks: " + std::string(e.what())).c_str());
return 1;
}
} else {
// If no hooks directory provided, create empty hook script templates
std::vector<std::string> hook_names = {
"PRE-INSTALL", "PRE-INSTALL_ROLLBACK",
"POST-INSTALL", "POST-INSTALL_ROLLBACK",
"PRE-UPDATE", "PRE-UPDATE_ROLLBACK",
"POST-UPDATE", "POST-UPDATE_ROLLBACK",
"PRE-REMOVE", "PRE-REMOVE_ROLLBACK",
"POST-REMOVE", "POST-REMOVE_ROLLBACK"
};
dpm_log(LOG_INFO, "Creating empty hook templates");
for (const auto& hook_name : hook_names) {
std::filesystem::path hook_path = package_dir / "hooks" / hook_name;
std::ofstream hook_file(hook_path);
if (hook_file.is_open()) {
hook_file << "#!/bin/sh\n";
hook_file << "# " << hook_name << " hook for " << package_name << "\n";
hook_file << "# This is a template. Modify as needed.\n\n";
hook_file << "# Exit with non-zero status to indicate failure\n";
hook_file << "exit 0\n";
hook_file.close();
// Make the hook file executable
chmod(hook_path.c_str(), 0755);
} else {
dpm_log(LOG_ERROR, ("Failed to create hook file: " + hook_path.string()).c_str());
}
}
}
// Create basic metadata files
try {
std::filesystem::path metadata_dir = package_dir / "metadata";
// Create NAME file
{
std::ofstream name_file(metadata_dir / "NAME");
if (name_file.is_open()) {
name_file << package_name;
name_file.close();
}
}
// Create VERSION file
{
std::ofstream version_file(metadata_dir / "VERSION");
if (version_file.is_open()) {
version_file << package_version;
version_file.close();
}
}
// Create ARCHITECTURE file
{
std::ofstream arch_file(metadata_dir / "ARCHITECTURE");
if (arch_file.is_open()) {
arch_file << architecture;
arch_file.close();
}
}
// Create empty placeholder files for other metadata
std::vector<std::string> metadata_files = {
"AUTHOR", "MAINTAINER", "DEPENDENCIES", "DESCRIPTION",
"CONTENTS_MANIFEST_DIGEST", "LICENSE", "PACKAGE_DIGEST",
"HOOKS_DIGEST", "PROVIDES", "REPLACES", "SOURCE", "CHANGELOG"
};
for (const auto& file_name : metadata_files) {
std::ofstream metadata_file(metadata_dir / file_name);
metadata_file.close();
}
dpm_log(LOG_INFO, "Created metadata files");
} catch (const std::exception& e) {
dpm_log(LOG_ERROR, ("Failed to create metadata files: " + std::string(e.what())).c_str());
return 1;
}
dpm_log(LOG_INFO, "Package staging completed successfully");
dpm_log(LOG_INFO, ("Package staged at: " + package_dir.string()).c_str());
dpm_log(LOG_INFO, "Next steps:");
dpm_log(LOG_INFO, "1. Edit metadata files to provide package information");
dpm_log(LOG_INFO, "2. Update CONTENTS_MANIFEST_DIGEST to mark controlled/non-controlled files");
dpm_log(LOG_INFO, "3. Customize hook scripts as needed");
return 0;
}