Compare commits

..

61 commits

Author SHA1 Message Date
netkas
b975508d07 Indentation Correction 2025-01-08 15:46:13 -05:00
netkas
1ef96c3c4c Updated CHANGELOG.md 2024-10-29 12:28:41 -04:00
netkas
2dad5133bd Updated .gitignore 2024-10-25 19:59:33 -04:00
netkas
544d4ebdf6 Validate package instance before checking execution policy 2024-10-25 19:59:22 -04:00
netkas
41947069be Bumped version to 2.1.6 2024-10-25 19:56:25 -04:00
netkas
11aaa39eaa Update build script and bump version to 2.1.5 2024-10-14 15:29:32 -04:00
bdcf510089 Finalized CHANGELOG.md 2024-10-13 14:50:32 -04:00
278b3d0e2e Add executable and compressed templates with CI updates 2024-10-13 14:50:16 -04:00
771748636d Executable Compiler will now correctly create a directory if it does not exist when compiling a package using gcc 2024-10-13 12:31:51 -04:00
netkas
3b5a3882f6 Refactor CI templates to support dynamic build targets 2024-10-12 14:54:48 -04:00
netkas
77c668f7b6 Refactor phpmake template to support dynamic build targets 2024-10-11 14:21:10 -04:00
netkas
c40c7ce8fe Added new dynamic constant %BUILD_OUTPUT_PATH% which can be used as %BUILD_OUTPUT_PATH%:release to show the output path of a specific build configuration 2024-10-10 15:31:59 -04:00
netkas
f5433c6bf9 Added new constant '%DEFAULT_BUILD_CONFIGURATION%' which points to the project's default build configuration 2024-10-10 15:17:21 -04:00
netkas
bc86e5a65d Updated CHANGELOG.md 2024-10-10 14:16:25 -04:00
netkas
f8ede74764 Updated Runtime 2024-10-10 14:13:47 -04:00
netkas
c87ebf14dd Bumped version to 2.1.4 2024-09-30 03:13:46 -04:00
netkas
e48e6a55e4 Null-pointer fix 2024-09-27 14:00:15 -04:00
netkas
08c770c9e1 Bumped version 2024-09-27 13:57:56 -04:00
netkas
e2a8dbfe27 Implement pre-install and post-install unit execution 2024-09-27 13:23:46 -04:00
netkas
91fe129bf4 Add getter methods for installation lifecycle steps 2024-09-27 00:42:33 -04:00
netkas
44dc1498f7 Updated CHANGELOG.md 2024-09-27 00:39:47 -04:00
netkas
c659baa9ce Updated DocStrings in PackageManager 2024-09-27 00:39:21 -04:00
netkas
1b33b83926 Fixed incorrect enum usage in RepositoryMenu 2024-09-27 00:35:01 -04:00
netkas
9303158674 Bumped version to version 2.1.2 2024-09-27 00:33:17 -04:00
netkas
d249e99448 Merge remote-tracking branch 'origin/dev' into dev 2024-09-24 12:53:33 -04:00
netkas
3f2f325bcc Updated CHANGELOG.md 2024-09-24 12:52:51 -04:00
netkas
2a20786755 Updated github_ci.yml.tpl 2024-09-24 12:52:51 -04:00
netkas
8ceb6756a4 Updated github_ci.yml.tpl 2024-09-24 12:48:16 -04:00
netkas
51bdb22540 Updated github_ci.yml.tpl 2024-09-24 12:45:09 -04:00
netkas
36d8e6f8c6 Updated github_ci.yml.tpl 2024-09-24 12:42:04 -04:00
netkas
9136396700 Updated github_ci.yml.tpl 2024-09-24 12:39:36 -04:00
netkas
e225add40b Updated github_ci.yml.tpl 2024-09-24 12:37:19 -04:00
netkas
33865d7e48 Updated github_ci.yml.tpl 2024-09-24 12:35:25 -04:00
netkas
cba6942cca Updated github_ci.yml.tpl 2024-09-24 12:32:27 -04:00
netkas
29e0a6e4ae Updated github_ci.yml.tpl 2024-09-24 00:36:31 -04:00
netkas
cba5f7041a Updated github_ci.yml.tpl 2024-09-24 00:34:15 -04:00
netkas
1ddb042175 Updated github_ci.yml.tpl 2024-09-24 00:30:09 -04:00
netkas
093e3341e1 Create class directory if it does not exist 2024-09-24 00:26:31 -04:00
netkas
80a019452f Add GitHub CI template support 2024-09-23 18:56:28 -04:00
netkas
b30ddbdc28 Seperated Makefile template into it's own template, added phpcli_full & phplib_full to combine all Templates together for building a PHP project 2024-09-23 15:17:15 -04:00
netkas
e829a839d1 Updated CHANGELOG.md 2024-09-23 15:05:51 -04:00
netkas
d813ef7b94 Added new template for PHP called "phpunit" to generate PHPUnit bootstrap test 2024-09-23 15:01:56 -04:00
netkas
09fb388ee7 Bumped version to 2.1.1 2024-09-20 20:12:38 -04:00
netkas
87290232f5 Merge branch 'master' into dev
# Conflicts:
#	CHANGELOG.md
#	src/ncc/CLI/Commands/BuildCommand.php
#	src/ncc/Classes/GithubExtension/GithubRepository.php
#	src/ncc/Managers/PackageManager.php
#	src/ncc/VERSION
#	src/ncc/version.json
2024-09-20 20:10:15 -04:00
a65b76b6bf
Updated CHANGELOG.md 2023-10-25 22:02:26 -04:00
957d9a9510
Added the ability to use 'all' as a build configuration when running ncc build, to build all build configurations
in the project.

The build command in ncc has been updated to accept 'all' as a build configuration which prompts the build of all configurations in the project. Previously, builds had to be triggered for each individual configurations. This change simplifies the build process especially for projects with multiple configurations, making the process more efficient and less prone to human error.
2023-10-25 22:00:37 -04:00
d2635b19fd
Fixed issue when creating a shadow copy of a package, if the universe aligns together and the cosmos unit together to
produce a package length exactly to where the end-of-package byte sequence is cut in half, the shadow copy will fail
   to be created due to the end-of-package byte sequence being cut in half, this issue was fixed by reading the package
   in chunks to determine the end-of-package byte sequence.

The PackageReader class has been updated to use a more efficient buffer management when reading data from a file. Previously, buffer size was growing uncontrollably and can eventually lead to out-of-memory errors for large files.

Now, the data is read in chunks and the buffer is cleared when it exceeds approximately 1MB size, maintaining only the last 512KB. This change ensures a more memory-efficient package reading and effectively prevents erroneous deadlocks for large package files.

Additionally, detection for end-of-data byte sequence has been modified to rectify an issue where package length could cut off the end-of-package byte sequence. This results in an improved reliability for package validation.
2023-10-25 21:40:42 -04:00
76f12bb0a3
Fixed issue where all development dependencies were not correctly being added to debug builds in composer projects,
instead these dependencies were added globally to the build configuration. This issue was fixed by adding all the
   development dependencies to the debug build configurations only.

This commit revises the handling of composer dependencies in 'ProjectManager.php'. A problem was encountered where software development dependencies were added globally to the build configuration, rather than exclusively to debug builds. By reassigning these dependencies to the debug configuration, this issue has been remedied.

Additionally, a bug-fix in 'PackageReader.php' has been implemented to validate the package file by checking the data length. If the file is discovered to be invalid (data length is null or zero), an IOException is triggered.

These changes enhance the accuracy of dependency management and improve the robustness of package validation.
2023-10-25 17:57:51 -04:00
7befd995e7
Added host resolving in network calls to improve the handling of invalid or unreachable URLs
The 'Resolver' utility was utilized to resolve the host and cache the result, enhancing the robustness and efficiency of the network calling mechanism across different modules including PackageManager, GiteaRepository, GithubRepository, and others.
2023-10-25 15:08:58 -04:00
b2234d5040
Updated the Download function to attempt to retry the download upon an error for at least 3 times.
Updated the downloadFile function in PackageManager.php to retry the download upon an error for at least 3 times. This improvement on error handling will enhance the resilience of the download process, reducing the likelihood of a single network hiccup resulting in a failed download. This change has also been reflected in the CHANGELOG.md file.
2023-10-25 14:33:42 -04:00
27d1609b23
Added Project.xml 2023-10-25 14:00:46 -04:00
de1e199dff
Fixed issue where a newline is sometimes added to the end of a download output due to how short the download process was, mitigated the issue by enforcing a "done" update at the end of the download process
A fix has been implemented for an issue where a newline was unintentionally added to the end of download output, mostly when the download process was too short. This has been mitigated by ensuring a "done" update whenever a download finishes.

At the same time, improvements have been made to the download progress output. Specifically, the downloaded size now won't exceed the total download size, and a definitive "100%" progress indication is enforced when the download ends.
2023-10-25 14:00:24 -04:00
aa6800e96a
Correction 2023-10-24 16:41:41 -04:00
a2cd98ba98
Updated exception handling in PackageReader
Optimized exception handling in PackageReader class by replacing NotSupportedException with IntegrityException. The update aligns with the objective of more accurate error handling and reporting during ZiProto decoding. The new IntegrityException provides a detailed error message, facilitating easier debugging.
2023-10-24 16:28:10 -04:00
27baeca112
Set default process timeouts to null
This commit sets the default timeout and idleTimeout for the execution process in ExecutionUnitRunner to null when there are no specific timeouts provided by the execution policy. This change was made to avoid unexpected timeouts when no specific values are set in the execution policy.
2023-10-24 16:07:48 -04:00
4f7aa7a859
Update progress bar text to display basename only
Modified the progress bar text display in PackageManager.php and NccCompiler.php classes. Changed 'setMiscText' function to now display only the basename of the component name, execution policy name, and resource name. This change improves readability and clarity of the progress bar by reducing clutter from long file paths.
2023-10-24 16:05:40 -04:00
3ebdeb0cba
Fixed issue where progress bar is displayed in VERBOSE mode
Added checks in the update method of the ConsoleProgressBar class to prevent it from displaying when log level is set to VERBOSE. This is to prevent cluttering the command-line interface with unnecessary information when running in VERBOSE mode. The changes also are reflected in the changelog.
2023-10-22 22:16:33 -04:00
bcc6f45eb4
Fix division by zero in ConsoleProgressBar
The rendering of the progress bar was causing a division by zero error when the maximum value was set to 0. This change adds a condition to check if the maximum value is not 0 before calculating the number of hashes and the percentage done in the progress bar. This avoids the division by zero error and makes the progress bar rendering more robust. The CHANGELOG.md file has also been updated to reflect this bug fix.
2023-10-22 19:26:16 -04:00
698d2e7a1f
Updated file tracking in Runtime class
Implemented changes in Runtime.php to better handle file tracking and to prevent unnecessary inclusion of duplicate files during Runtime. Instead of directly checking if a file is already included, we now create a unique identifier for each file using a crc32 hash function. This identifier (instead of the file path) is checked and stored in the inclusion list, allowing for better handling of dynamic or virtual files.
2023-10-18 16:03:40 -04:00
89d3af8680
Improve build efficiency by preventing duplicate merges
Introduced a private property to the `NccCompiler` class, `$merged_dependencies`, to keep track of the dependencies that have already been merged. This prevents unnecessary re-merging during the build operation, potentially enhancing the efficiency of the process. The implementation involves a check for a preexisting merge before a new merge is performed. If a merge already exists, the process is skipped, thereby avoiding redundancies.
2023-10-18 15:56:24 -04:00
12d7744e1e
Bumped version to 2.0.4 2023-10-18 15:49:16 -04:00
40 changed files with 1522 additions and 110 deletions

1
.gitignore vendored
View file

@ -31,3 +31,4 @@ tests/example_project/ncc
tests/example_project/build
/.phpunit.result.cache
/.idea/php-test-framework.xml
/.idea/gbrowser_project.xml

7
.idea/codeStyles/Project.xml generated Normal file
View file

@ -0,0 +1,7 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<ScalaCodeStyleSettings>
<option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
</ScalaCodeStyleSettings>
</code_scheme>
</component>

View file

@ -5,6 +5,83 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.1.6] - 2024-10-29
This update introduces critical bug fixes
### Fixed
- Validate package instance before checking execution policy
## [2.1.5] - 2024-10-14
This update introduces a critical bug fix
## [2.1.4] - 2024-10-13
This update introduces minor bug fixes & improvements
### Added
- Added new constant `%DEFAULT_BUILD_CONFIGURATION%` which points to the project's default build configuration
- Added new dynamic constant `%BUILD_OUTPUT_PATH%` which can be used as `%BUILD_OUTPUT_PATH%:release` to show the
output path of a specific build configuration
- Refactor CI templates to support dynamic build targets
- Added template `phpexe` & `phpgz` for generating executable binaries and compressed executables for the project
### Changed
- Refactor phpmake template to support dynamic build targets
### Fixed
- ncc will now correctly handle package execution where the main unit is not defined in the package instead of
throwing an exception.
- Executable Compiler will now correctly create a directory if it does not exist when compiling a package using gcc
## [2.1.3] - 2024-09-27
This update introduces bug fixes
### Fixed
- Null-pointer fix
## [2.1.2] - 2024-09-27
This update introduces bug fixes
### Added
- Add getter methods for installation lifecycle steps
- Added pre-install & post-install execution unit handling
### Changed
- Updated DocStrings in PackageManager
### Fixed
- Fixed incorrect enum usage in RepositoryMenu
## [2.1.1] - 2024-09-24
This update introduces a couple of new features and quality of life improvements
### Added
- Added new PHP template `phpunit` for generating PhpUnit tests for the project
- Added new PHP template `phpmake` for generating a Makefile for the project
- Added new PHP template `phplib_full` That combines `phplib`, `phpmake` and `phpunit` templates
- Added new PHP template `phpcli_full` That combines `phpcli`, `phplib`, `phpmake` and `phpunit` templates
- Added new PHP template `phpci_github` that generates a Github CI workflow for the project
### Changed
- The template `phpcli` no longer includes a Makefile, instead use `phpmake` to generate a Makefile for the project
### Fixed
- Fixed incorrect enum usage with a logging method call.
## [2.1.0] - 2024-09-20
This update introduces a refactored code-base, code quality improvements, and better exception handling.

View file

@ -2,7 +2,7 @@ Package: ncc
Source: ncc
Section: devel
Priority: optional
Version: 2.0.3
Version: 2.0.4
Maintainer: netkas <netkas@nosial.net>
Build-Depends: debhelper-compat (= 13)
Standards-Version: 4.5.1

View file

@ -119,7 +119,7 @@
$output = sprintf('%s (%s) [%s]',
$source->getName(),
Console::formatColor($source->getHost(), ConsoleColors::GREEN),
Console::formatColor($source->getType(), ConsoleColors::YELLOW)
Console::formatColor($source->getType()->value, ConsoleColors::YELLOW)
);
if(!$source->isSsl())

View file

@ -31,6 +31,7 @@
use ncc\Classes\PhpExtension\PhpRunner;
use ncc\Enums\Runners;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IntegrityException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\NotSupportedException;
use ncc\Exceptions\OperationException;
@ -76,11 +77,19 @@
{
$process->setTimeout($unit->getExecutionPolicy()->getExecute()->getTimeout());
}
else
{
$process->setTimeout(null);
}
if($unit->getExecutionPolicy()->getExecute()->getIdleTimeout() !== null)
{
$process->setIdleTimeout($unit->getExecutionPolicy()->getExecute()->getIdleTimeout());
}
else
{
$process->setIdleTimeout(null);
}
return $process;
}
@ -129,6 +138,7 @@
* @return int
* @throws ConfigurationException
* @throws OperationException
* @throws IntegrityException
*/
public static function executeFromPackage(PackageReader $package_reader, string $policy_name, array $args=[]): int
{

View file

@ -39,6 +39,7 @@
use ncc\Objects\Vault\Password\AccessToken;
use ncc\Objects\Vault\Password\UsernamePassword;
use ncc\Utilities\Console;
use ncc\Utilities\Resolver;
use ncc\Utilities\RuntimeCache;
use RuntimeException;
@ -97,12 +98,18 @@
'User-Agent: ncc'
];
if($authentication !== null)
{
$headers = self::injectAuthentication($authentication, $curl, $headers);
}
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => HttpRequestType::GET->value,
@ -186,6 +193,13 @@
$headers = self::injectAuthentication($authentication, $curl, $headers);
}
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_URL => $endpoint,
CURLOPT_RETURNTRANSFER => true,
@ -246,6 +260,13 @@
$headers = self::injectAuthentication($authentication, $curl, $headers);
}
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_URL => $endpoint,
CURLOPT_RETURNTRANSFER => true,
@ -337,6 +358,13 @@
$headers = self::injectAuthentication($authentication, $curl, $headers);
}
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_URL => $endpoint,
CURLOPT_RETURNTRANSFER => true,
@ -433,6 +461,13 @@
$headers = self::injectAuthentication($authentication, $curl, $headers);
}
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_URL => $endpoint,
CURLOPT_RETURNTRANSFER => true,

View file

@ -39,6 +39,7 @@
use ncc\Objects\Vault\Password\AccessToken;
use ncc\Objects\Vault\Password\UsernamePassword;
use ncc\Utilities\Console;
use ncc\Utilities\Resolver;
use ncc\Utilities\RuntimeCache;
use RuntimeException;
@ -103,6 +104,13 @@
$headers = self::injectAuthentication($authentication, $curl, $headers);
}
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_URL => $endpoint,
CURLOPT_RETURNTRANSFER => true,
@ -187,6 +195,13 @@
$headers = self::injectAuthentication($authentication, $curl, $headers);
}
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_NOBODY => true,
@ -250,6 +265,13 @@
$headers = self::injectAuthentication($authentication, $curl, $headers);
}
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => HttpRequestType::GET->value,
@ -333,6 +355,13 @@
$headers = self::injectAuthentication($authentication, $curl, $headers);
}
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => HttpRequestType::GET->value,
@ -418,6 +447,13 @@
$headers = self::injectAuthentication($authentication, $curl, $headers);
}
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => HttpRequestType::GET->value,

View file

@ -22,8 +22,10 @@
namespace ncc\Classes\NccExtension;
use InvalidArgumentException;
use ncc\Enums\SpecialConstants\BuildConstants;
use ncc\Enums\SpecialConstants\DateTimeConstants;
use ncc\Enums\SpecialConstants\GeneralConstants;
use ncc\Enums\SpecialConstants\InstallConstants;
use ncc\Enums\SpecialConstants\AssemblyConstants;
use ncc\Enums\SpecialConstants\RuntimeConstants;
@ -47,6 +49,67 @@
$input = self::compileBuildConstants($input);
$input = self::compileDateTimeConstants($input, time());
$input = self::compileRuntimeConstants($input);
$input = self::compileGeneralConstants($input, $project_configuration);
return $input;
}
/**
* Compiles general constants from the given input string based on the provided project configuration.
*
* @param string|null $input The input string containing constants to be compiled.
* @param ProjectConfiguration $project_configuration The project configuration used to resolve constants.
* @return string|null The input string with constants replaced, or null if the input was null.
*/
public static function compileGeneralConstants(?string $input, ProjectConfiguration $project_configuration): ?string
{
if ($input === null)
{
return null;
}
// Replace %DEFAULT_BUILD_CONFIGURATION%
$input = str_replace(
[
GeneralConstants::DEFAULT_BUILD_CONFIGURATION->value
],
[
$project_configuration->getBuild()->getDefaultConfiguration()
],
$input
);
if (str_starts_with($input, GeneralConstants::BUILD_OUTPUT_PATH->value))
{
$build_name = null;
if (preg_match('/' . preg_quote(GeneralConstants::BUILD_OUTPUT_PATH->value, '/') . ':(\S+)/', $input, $matches))
{
$build_name = $matches[1];
}
if ($build_name === null)
{
Console::outWarning(sprintf("Cannot compile constant %s because it's not valid, usage: %%CONSTANT%%:<name>", $input));
}
else
{
try
{
$output_path = $project_configuration->getBuild()->getBuildConfiguration($build_name)->getOutput();
$input = preg_replace(
'/' . preg_quote(GeneralConstants::BUILD_OUTPUT_PATH->value, '/') . ':\S+/',
$output_path,
$input,
1
);
}
catch (InvalidArgumentException $e)
{
Console::outError(sprintf("Cannot compile constant %s because it does not point to an existing build configuration name", $input));
}
}
}
return $input;
}
@ -70,7 +133,7 @@
AssemblyConstants::ASSEMBLY_NAME->value,
AssemblyConstants::ASSEMBLY_PACKAGE->value,
AssemblyConstants::ASSEMBLY_VERSION->value,
AssemblyConstants::ASSEMBLY_UID->value
AssemblyConstants::ASSEMBLY_UID->value,
],
[
$assembly->getName(),

View file

@ -59,11 +59,17 @@
*/
private $project_manager;
/**
* @var array
*/
private $merged_dependencies;
/**
* @param ProjectManager $project_manager
*/
public function __construct(ProjectManager $project_manager)
{
$this->merged_dependencies = [];
$this->project_manager = $project_manager;
}
@ -89,6 +95,7 @@
*/
public function build(string $build_configuration=BuildConfigurationValues::DEFAULT->value, array $options=[]): string
{
$this->merged_dependencies = [];
$configuration = $this->project_manager->getProjectConfiguration()->getBuild()->getBuildConfiguration($build_configuration);
$configuration->setOptions(array_merge($configuration->getOptions(), $options));
$static_dependencies = isset($configuration->getOptions()[BuildConfigurationOptions::STATIC_DEPENDENCIES->value]);
@ -164,7 +171,7 @@
foreach($execution_units as $unit)
{
$progress_bar->setMiscText($unit->getExecutionPolicy()->getName());
$progress_bar->setMiscText(basename($unit->getExecutionPolicy()->getName()));
//$progress++;
//Console::inlineProgressBar($progress, $steps);
$package_writer->addExecutionUnit($unit);
@ -177,7 +184,7 @@
{
//$progress++;
//Console::inlineProgressBar($progress, $steps);
$progress_bar->setMiscText($component);
$progress_bar->setMiscText(basename($component));
Console::outVerbose(sprintf('Compiling \'%s\'', $component));
$this->processComponent($package_writer, $component);
@ -189,7 +196,7 @@
{
//$progress++;
//Console::inlineProgressBar($progress, $steps);
$progress_bar->setMiscText($resource);
$progress_bar->setMiscText(basename($resource));
Console::outVerbose(sprintf('Processing \'%s\'', $resource));
$this->processResource($package_writer, $resource);
@ -234,13 +241,14 @@
/** @noinspection UnusedFunctionResultInspection */
$package_writer->addDependencyConfiguration($dependency);
if(!$static)
if(!$static || in_array($dependency->getName(), $this->merged_dependencies, true))
{
return;
}
$entry = (new PackageManager())->getPackageLock()->getVersionEntry($dependency->getName(), $dependency->getVersion());
$package_writer->merge((new PackageReader($entry->getShadowPackagePath($dependency->getName()))));
$this->merged_dependencies[] = $dependency->getName();
foreach($entry->getDependencies() as $sub_dependency)
{

View file

@ -29,8 +29,8 @@
use ncc\Enums\Flags\PackageFlags;
use ncc\Enums\PackageDirectory;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IntegrityException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\NotSupportedException;
use ncc\Objects\Package\Component;
use ncc\Objects\Package\ExecutionUnit;
use ncc\Objects\Package\Metadata;
@ -84,6 +84,11 @@
*/
private $package_file;
/**
* @var string
*/
private $package_path;
/**
* @var array
*/
@ -102,7 +107,9 @@
throw new IOException(sprintf('File \'%s\' does not exist', $file_path));
}
$this->package_path = $file_path;
$this->package_file = fopen($file_path, 'rb');
if($this->package_file === false)
{
throw new IOException(sprintf('Failed to open file \'%s\'', $file_path));
@ -175,20 +182,31 @@
// Seek the data until the end of the package (FF AA 55 F0)
fseek($this->package_file, $this->data_offset);
$buffer = '';
while(!feof($this->package_file))
{
$buffer = fread($this->package_file, 1024);
$this->data_length += strlen($buffer);
$current_chunk = fread($this->package_file, 1024);
$this->data_length += strlen($current_chunk);
$buffer .= $current_chunk;
// If we detect the end-of-data byte sequence
if (($position = strpos($buffer, "\xFF\xAA\x55\xF0")) !== false)
{
$this->data_length -= strlen($buffer) - $position;
$this->package_length += $this->data_length + 4;
break;
}
// Check if the buffer is 1MB or larger
if(strlen($buffer) > 1048576)
{
// Remove the first 512kb of the buffer
$buffer = substr($buffer, 512000);
}
if($this->data_length === null)
}
if($this->data_length === null || $this->data_length === 0)
{
throw new IOException(sprintf('File \'%s\' is not a valid package file (missing end of package)', $file_path));
}
@ -257,7 +275,7 @@
{
if(!isset($this->headers[PackageStructure::DIRECTORY->value][$name]))
{
throw new RuntimeException(sprintf('File \'%s\' not found in package', $name));
throw new RuntimeException(sprintf('File \'%s\' not found in package \'%s\'', $name, $this->package_path));
}
$location = explode(':', $this->headers[PackageStructure::DIRECTORY->value][$name]);
@ -281,7 +299,7 @@
{
if(!isset($this->headers[PackageStructure::DIRECTORY->value][$name]))
{
throw new RuntimeException(sprintf('Resource \'%s\' not found in package', $name));
throw new RuntimeException(sprintf('Resource \'%s\' not found in package \'%s\'', $name, $this->package_path));
}
$location = explode(':', $this->headers[PackageStructure::DIRECTORY->value][$name]);
@ -317,6 +335,7 @@
*
* @return Assembly
* @throws ConfigurationException
* @throws IntegrityException
*/
public function getAssembly(): Assembly
{
@ -329,10 +348,18 @@
if(!isset($this->headers[PackageStructure::DIRECTORY->value][$directory]))
{
throw new ConfigurationException('Package does not contain an assembly');
throw new ConfigurationException(sprintf('Assembly object not found in package \'%s\'', $this->package_path));
}
try
{
$assembly = Assembly::fromArray(ZiProto::decode($this->get($directory)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode assembly from package \'%s\' using ZiProto: %s', $this->package_path, $e->getMessage()), $e);
}
$this->cache[$directory] = $assembly;
return $assembly;
}
@ -342,7 +369,7 @@
*
* @return Metadata
* @throws ConfigurationException
* @throws NotSupportedException
* @throws IntegrityException
*/
public function getMetadata(): Metadata
{
@ -355,10 +382,18 @@
if(!isset($this->headers[PackageStructure::DIRECTORY->value][$directory]))
{
throw new ConfigurationException('Package does not contain metadata');
throw new ConfigurationException(sprintf('Metadata object not found in package \'%s\'', $this->package_path));
}
try
{
$metadata = Metadata::fromArray(ZiProto::decode($this->get($directory)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode metadata from package \'%s\' using ZiProto: %s', $this->package_path, $e->getMessage()), $e);
}
foreach($this->getFlags() as $flag)
{
$metadata->setOption($flag, true);
@ -372,6 +407,7 @@
* Optional. Returns the package's installer
*
* @return Installer|null
* @throws IntegrityException
*/
public function getInstaller(): ?Installer
{
@ -387,7 +423,15 @@
return null;
}
try
{
$installer = Installer::fromArray(ZiProto::decode($this->get($directory)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode installer from package \'%s\' using ZiProto: %s', $this->package_path, $e->getMessage()), $e);
}
$this->cache[$directory] = $installer;
return $installer;
}
@ -419,17 +463,25 @@
* @param string $name
* @return Dependency
* @throws ConfigurationException
* @throws IntegrityException
*/
public function getDependency(string $name): Dependency
{
$dependency_name = sprintf('@%s:%s', PackageDirectory::DEPENDENCIES->value, $name);
if(!isset($this->headers[PackageStructure::DIRECTORY->value][$dependency_name]))
{
throw new ConfigurationException(sprintf('Dependency \'%s\' not found in package', $name));
throw new ConfigurationException(sprintf('Dependency \'%s\' not found in package \'%s\'', $name, $this->package_path));
}
try
{
return Dependency::fromArray(ZiProto::decode($this->get($dependency_name)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode dependency \'%s\' from package \'%s\' using ZiProto: %s', $name, $this->package_path, $e->getMessage()), $e);
}
}
/**
* Returns a dependency from the package by pointer
@ -437,12 +489,19 @@
* @param int $pointer
* @param int $length
* @return Dependency
* @throws ConfigurationException
* @throws IntegrityException
*/
public function getDependencyByPointer(int $pointer, int $length): Dependency
{
try
{
return Dependency::fromArray(ZiProto::decode($this->getByPointer($pointer, $length)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode dependency from pointer \'%s\' with length \'%s\' from package \'%s\' using ZiProto: %s', $pointer, $length, $this->package_path, $e->getMessage()), $e);
}
}
/**
* Returns an array of execution units from the package
@ -471,17 +530,36 @@
* @param string $name
* @return ExecutionUnit
* @throws ConfigurationException
* @throws IntegrityException
*/
public function getExecutionUnit(string $name): ExecutionUnit
{
$execution_unit_name = sprintf('@%s:%s', PackageDirectory::EXECUTION_UNITS->value, $name);
if(!isset($this->headers[PackageStructure::DIRECTORY->value][$execution_unit_name]))
{
throw new ConfigurationException(sprintf('Execution unit \'%s\' not found in package', $name));
throw new ConfigurationException(sprintf('Execution unit \'%s\' not found in package \'%s\'', $name, $this->package_path));
}
try
{
return ExecutionUnit::fromArray(ZiProto::decode($this->get($execution_unit_name)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode execution unit \'%s\' from package file \'%s\' using ZiProto: %s', $name, $this->package_path, $e->getMessage()), $e);
}
}
/**
* Checks if an execution unit with the specified name exists in the package.
*
* @param string $name The name of the execution unit to check.
* @return bool True if the execution unit exists, false otherwise.
*/
public function executionUnitExists(string $name): bool
{
return isset($this->headers[PackageStructure::DIRECTORY->value][sprintf('@%s:%s', PackageDirectory::EXECUTION_UNITS->value, $name)]);
}
/**
* Returns an execution unit from the package by pointer
@ -489,12 +567,19 @@
* @param int $pointer
* @param int $length
* @return ExecutionUnit
* @throws ConfigurationException
* @throws IntegrityException
*/
public function getExecutionUnitByPointer(int $pointer, int $length): ExecutionUnit
{
try
{
return ExecutionUnit::fromArray(ZiProto::decode($this->getByPointer($pointer, $length)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode execution unit from pointer \'%s\' with length \'%s\' from package \'%s\' using ZiProto: %s', $pointer, $length, $this->package_path, $e->getMessage()), $e);
}
}
/**
* Returns the package's component pointers
@ -544,17 +629,25 @@
* @param string $name
* @return Component
* @throws ConfigurationException
* @throws IntegrityException
*/
public function getComponent(string $name): Component
{
$component_name = sprintf('@%s:%s', PackageDirectory::COMPONENTS->value, $name);
if(!isset($this->headers[PackageStructure::DIRECTORY->value][$component_name]))
{
throw new ConfigurationException(sprintf('Component \'%s\' not found in package', $name));
throw new ConfigurationException(sprintf('Component \'%s\' not found in package \'%s\'', $name, $this->package_path));
}
try
{
return Component::fromArray(ZiProto::decode($this->get($component_name)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode component \'%s\' from package \'%s\' using ZiProto: %s', $name, $this->package_path, $e->getMessage()), $e);
}
}
/**
* Returns a component from the package by pointer
@ -562,12 +655,19 @@
* @param int $pointer
* @param int $length
* @return Component
* @throws ConfigurationException
* @throws IntegrityException
*/
public function getComponentByPointer(int $pointer, int $length): Component
{
try
{
return Component::fromArray(ZiProto::decode($this->getByPointer($pointer, $length)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode component from pointer \'%s\' with length \'%s\' from package \'%s\' using ZiProto: %s', $pointer, $length, $this->package_path, $e->getMessage()), $e);
}
}
/**
* Returns a component from the package by a class pointer
@ -575,17 +675,25 @@
* @param string $class
* @return Component
* @throws ConfigurationException
* @throws IntegrityException
*/
public function getComponentByClass(string $class): Component
{
$class_name = sprintf('@%s:%s', PackageDirectory::CLASS_POINTER->value, $class);
if(!isset($this->headers[PackageStructure::DIRECTORY->value][$class_name]))
{
throw new ConfigurationException(sprintf('Class map \'%s\' not found in package', $class));
throw new ConfigurationException(sprintf('Class map \'%s\' not found in package \'%s\'', $class, $this->package_path));
}
try
{
return Component::fromArray(ZiProto::decode($this->get($class_name)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode component from class pointer \'%s\' from package \'%s\' using ZiProto: %s', $class, $this->package_path, $e->getMessage()), $e);
}
}
/**
* Returns an array of resource pointers from the package
@ -614,17 +722,25 @@
* @param string $name
* @return Resource
* @throws ConfigurationException
* @throws IntegrityException
*/
public function getResource(string $name): Resource
{
$resource_name = sprintf('@%s:%s', PackageDirectory::RESOURCES->value, $name);
if(!isset($this->headers[PackageStructure::DIRECTORY->value][$resource_name]))
{
throw new ConfigurationException(sprintf('Resource \'%s\' not found in package', $name));
throw new ConfigurationException(sprintf('Resource \'%s\' not found in package \'%s\'', $name, $this->package_path));
}
try
{
return Resource::fromArray(ZiProto::decode($this->get($resource_name)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode resource \'%s\' from package \'%s\' using ZiProto: %s', $name, $this->package_path, $e->getMessage()), $e);
}
}
/**
* Returns a resource from the package by pointer
@ -632,12 +748,19 @@
* @param int $pointer
* @param int $length
* @return Resource
* @throws ConfigurationException
* @throws IntegrityException
*/
public function getResourceByPointer(int $pointer, int $length): Resource
{
try
{
return Resource::fromArray(ZiProto::decode($this->getByPointer($pointer, $length)));
}
catch(Exception $e)
{
throw new IntegrityException(sprintf('Failed to decode resource from pointer \'%s\' with length \'%s\' from package \'%s\' using ZiProto: %s', $pointer, $length, $this->package_path, $e->getMessage()), $e);
}
}
/**
* Searches the package's directory for a file that matches the given filename
@ -755,7 +878,7 @@
fseek($this->package_file, $this->package_offset);
$remaining_bytes = $this->package_length;
while ($remaining_bytes > 0)
while($remaining_bytes > 0)
{
$bytes_to_read = min($remaining_bytes, 4096);
$data = fread($this->package_file, $bytes_to_read);

View file

@ -38,6 +38,7 @@
use ncc\ThirdParty\composer\semver\Comparator;
use ncc\ThirdParty\composer\semver\Semver;
use ncc\Utilities\Console;
use ncc\Utilities\Resolver;
use ncc\Utilities\RuntimeCache;
use RuntimeException;
@ -70,6 +71,13 @@
'User-Agent: ncc'
];
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => HttpRequestType::GET->value,
@ -130,6 +138,13 @@
'User-Agent: ncc'
];
$resolved_host = Resolver::getResolveOption($endpoint);
if($resolved_host !== null)
{
curl_setopt($curl, CURLOPT_RESOLVE, [$resolved_host]);
}
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => HttpRequestType::GET->value,

View file

@ -84,6 +84,12 @@
$binary_path = ConstantCompiler::compileConstants($this->getProjectManager()->getProjectConfiguration(), $configuration->getOutput());
}
// Create the directory recursively if it does not exist
if(!is_dir(dirname($binary_path)))
{
mkdir(dirname($binary_path), 0777, true);
}
if($gcc_path === null)
{
throw new BuildException("Unable to find gcc executable, please make sure it is installed and in your PATH environment variable.");

View file

@ -74,7 +74,6 @@
self::writeProgramTemplate($project_manager);
self::writeMainEntryTemplate($project_manager);
self::writeMakefileTemplate($project_manager);
$project_manager->save();
}
@ -114,22 +113,4 @@
)
);
}
/**
* Writes the Makefile to the project directory
*
* @param ProjectManager $project_manager
* @return void
* @throws IOException
* @throws PathNotFoundException
*/
private static function writeMakefileTemplate(ProjectManager $project_manager): void
{
IO::fwrite(
$project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'Makefile',
ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(),
IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'Makefile.tpl')
)
);
}
}

View file

@ -0,0 +1,61 @@
<?php
/*
* Copyright (c) Nosial 2022-2024, all rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including without
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
namespace ncc\Classes\PhpExtension\Templates;
use ncc\Enums\Options\BuildConfigurationOptions;
use ncc\Enums\SpecialConstants\AssemblyConstants;
use ncc\Enums\Types\BuildOutputType;
use ncc\Exceptions\IOException;
use ncc\Interfaces\TemplateInterface;
use ncc\Managers\ProjectManager;
use ncc\Objects\ProjectConfiguration\Build\BuildConfiguration;
class CompressedTemplate implements TemplateInterface
{
/**
* @inheritDoc
* @throws IOException
*/
public static function applyTemplate(ProjectManager $project_manager): void
{
// Create the release build configuration
$release_compressed = new BuildConfiguration('release-compressed',
'build' . DIRECTORY_SEPARATOR . 'release' . DIRECTORY_SEPARATOR . AssemblyConstants::ASSEMBLY_PACKAGE->value . '.gz.ncc'
);
$release_compressed->setBuildType(BuildOutputType::NCC_PACKAGE->value);
$release_compressed->setOption(BuildConfigurationOptions::COMPRESSION->value, BuildConfigurationOptions\CompressionOptions::HIGH->value);
$project_manager->getProjectConfiguration()->getBuild()->addBuildConfiguration($release_compressed);
// Create the debug build configuration
$debug_compressed = new BuildConfiguration('debug-compressed',
'build' . DIRECTORY_SEPARATOR . 'debug' . DIRECTORY_SEPARATOR . AssemblyConstants::ASSEMBLY_PACKAGE->value . '.gz.ncc'
);
$debug_compressed->setBuildType(BuildOutputType::NCC_PACKAGE->value);
$debug_compressed->setOption(BuildConfigurationOptions::COMPRESSION->value, BuildConfigurationOptions\CompressionOptions::HIGH->value);
$debug_compressed->setDefinedConstant('DEBUG', '1');
$project_manager->getProjectConfiguration()->getBuild()->addBuildConfiguration($debug_compressed);
$project_manager->save();
}
}

View file

@ -0,0 +1,77 @@
<?php
/*
* Copyright (c) Nosial 2022-2024, all rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including without
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
namespace ncc\Classes\PhpExtension\Templates;
use ncc\Enums\Options\BuildConfigurationOptions;
use ncc\Enums\SpecialConstants\AssemblyConstants;
use ncc\Enums\Types\BuildOutputType;
use ncc\Exceptions\IOException;
use ncc\Interfaces\TemplateInterface;
use ncc\Managers\ProjectManager;
use ncc\Objects\ProjectConfiguration\Build\BuildConfiguration;
class ExecutableTemplate implements TemplateInterface
{
/**
* @inheritDoc
* @throws IOException
*/
public static function applyTemplate(ProjectManager $project_manager): void
{
foreach($project_manager->getProjectConfiguration()->getBuild()->getBuildConfigurations() as $build_configuration)
{
$executable_name = sprintf('%s-executable', $build_configuration);
$configuration = $project_manager->getProjectConfiguration()->getBuild()->getBuildConfiguration($build_configuration);
// Skip if the executable version of the build configuration already exists
if($project_manager->getProjectConfiguration()->getBuild()->buildConfigurationExists($executable_name))
{
continue;
}
// Skip if the build configuration is not an ncc package that the executable can be based on
if($configuration->getBuildType() !== BuildOutputType::NCC_PACKAGE->value)
{
continue;
}
if(isset($configuration->getOptions()[BuildConfigurationOptions::COMPRESSION->value]))
{
$output = dirname($configuration->getOutput()) . DIRECTORY_SEPARATOR . str_replace('-', '_', $executable_name);
}
else
{
$output = dirname($configuration->getOutput()) . DIRECTORY_SEPARATOR . str_replace('-', '_', $executable_name) . '_gz';
}
// Create the executable build configuration
$executable = new BuildConfiguration($executable_name, $output);
$executable->setBuildType(BuildOutputType::EXECUTABLE->value);
$executable->setOption(BuildConfigurationOptions::NCC_CONFIGURATION->value, $configuration->getName());
$project_manager->getProjectConfiguration()->getBuild()->addBuildConfiguration($executable);
}
$project_manager->save();
}
}

View file

@ -0,0 +1,117 @@
<?php
/*
* Copyright (c) Nosial 2022-2024, all rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including without
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
namespace ncc\Classes\PhpExtension\Templates;
use ncc\Classes\NccExtension\ConstantCompiler;
use ncc\Exceptions\IOException;
use ncc\Exceptions\PathNotFoundException;
use ncc\Managers\ProjectManager;
use ncc\Utilities\IO;
class GitHubWorkflowTemplate
{
/**
* @inheritDoc
* @param ProjectManager $project_manager
* @throws IOException
* @throws PathNotFoundException
*/
public static function applyTemplate(ProjectManager $project_manager): void
{
self::writeCiTemplate($project_manager);
}
/**
* Writes the Makefile to the project directory
*
* @param ProjectManager $project_manager
* @return void
* @throws IOException
* @throws PathNotFoundException
*/
private static function writeCiTemplate(ProjectManager $project_manager): void
{
$ci_dir = $project_manager->getProjectPath() . DIRECTORY_SEPARATOR . '.github' . DIRECTORY_SEPARATOR . 'workflows';
if(!file_exists($ci_dir))
{
mkdir($ci_dir, 0777, true);
}
$template = IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'github_ci.yml.tpl');
$default_configuration = $project_manager->getProjectConfiguration()->getBuild()->getDefaultConfiguration();
$builds = [];
$releases = [];
$downloads = [];
foreach($project_manager->getProjectConfiguration()->getBuild()->getBuildConfigurations() as $build_name)
{
$build_template = IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'github_ci_build.yml.tpl');
$build_template = str_replace('%TPL_BUILD_NAME%', $build_name, $build_template);
$build_template = str_replace('%TPL_BUILD_OUTPUT%',
$project_manager->getProjectConfiguration()->getBuild()->getBuildConfiguration($build_name)->getOutput(),
$build_template
);
$builds[$build_name] = $build_template;
$download_template = IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'github_ci_download.yml.tpl');
$download_template = str_replace('%TPL_BUILD_NAME%', $build_name, $download_template);
$downloads[$build_name] = $download_template;
$release_template = str_repeat(' ', 12) . $build_name . DIRECTORY_SEPARATOR . basename(
$project_manager->getProjectConfiguration()->getBuild()->getBuildConfiguration($build_name)->getOutput()
);
$releases[$build_name] = $release_template;
}
$build_jobs = '';
foreach($builds as $name => $build_template)
{
$build_jobs .= $build_template . PHP_EOL;
}
$download_jobs = '';
foreach($downloads as $name => $download_template)
{
$download_jobs .= $download_template . PHP_EOL;
}
$release_jobs = '';
foreach($releases as $name => $release_template)
{
$release_jobs .= $release_template . PHP_EOL;
}
$template = str_replace('%TPL_BUILDS%', $build_jobs, $template);
$template = str_replace('%TPL_DOWNLOAD_ARTIFACTS%', $download_jobs, $template);
$template = str_replace('%TPL_ARTIFACT_FILES%', $release_jobs, $template);
$template = str_replace('%TPL_BUILD_NAMES%', implode(', ', array_keys($builds)), $template);
$template = str_replace('%TPL_DEFAULT_BUILD_CONFIGURATION%', $default_configuration, $template);
$template = str_replace('%TPL_DEFAULT_ARTIFACT_BUILD_OUTPUT%', $default_configuration . DIRECTORY_SEPARATOR . basename(
$project_manager->getProjectConfiguration()->getBuild()->getBuildConfiguration($default_configuration)->getOutput()
), $template);
IO::fwrite($ci_dir . DIRECTORY_SEPARATOR . 'ncc_workflow.yml',
ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(), $template)
);
}
}

View file

@ -48,8 +48,14 @@
*/
private static function createClassTemplate(ProjectManager $project_manager): void
{
$class_directory = $project_manager->getProjectSourcePath() . DIRECTORY_SEPARATOR . $project_manager->getProjectConfiguration()->getAssembly()->getName();
if(!file_exists($class_directory))
{
mkdir($class_directory, 0777, true);
}
IO::fwrite(
$project_manager->getProjectSourcePath() . DIRECTORY_SEPARATOR . $project_manager->getProjectConfiguration()->getAssembly()->getName() . '.php',
$class_directory . DIRECTORY_SEPARATOR . $project_manager->getProjectConfiguration()->getAssembly()->getName() . '.php',
ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(),
IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'class.php.tpl')
)

View file

@ -1,20 +1,21 @@
# Variables
CONFIG ?= release
DEFAULT_CONFIGURATION ?= %TPL_DEFAULT_BUILD_CONFIGURATION%
LOG_LEVEL = debug
OUTDIR = build/$(CONFIG)
PACKAGE = $(OUTDIR)/%ASSEMBLY.PACKAGE%.ncc
# Default Target
all: build
all: %TPL_BUILD_NAMES%
# Build Steps
build:
ncc build --config=$(CONFIG) --log-level $(LOG_LEVEL)
%TPL_BUILDS%
install:
ncc package install --package=$(PACKAGE) --skip-dependencies --reinstall -y --log-level $(LOG_LEVEL)
install: %TPL_DEFAULT_BUILD_CONFIGURATION%
ncc package install --package=%TPL_DEFAULT_BUILD_PATH% --skip-dependencies --build-source --reinstall -y --log-level $(LOG_LEVEL)
test: %TPL_DEFAULT_BUILD_CONFIGURATION%
[ -f phpunit.xml ] || { echo "phpunit.xml not found"; exit 1; }
phpunit
clean:
rm -rf build
.PHONY: all build install clean
.PHONY: all install test clean %TPL_BUILD_NAMES%

View file

@ -0,0 +1,78 @@
<?php
/*
* Copyright (c) Nosial 2022-2024, all rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including without
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
namespace ncc\Classes\PhpExtension\Templates;
use ncc\Classes\NccExtension\ConstantCompiler;
use ncc\Exceptions\IOException;
use ncc\Exceptions\PathNotFoundException;
use ncc\Managers\ProjectManager;
use ncc\Utilities\IO;
class MakefileTemplate
{
/**
* @inheritDoc
* @param ProjectManager $project_manager
* @throws IOException
* @throws PathNotFoundException
*/
public static function applyTemplate(ProjectManager $project_manager): void
{
self::writeMakefileTemplate($project_manager);
}
/**
* Writes the Makefile template for the given project.
*
* @param ProjectManager $project_manager The project manager containing project configurations.
* @throws IOException If there is an error reading or writing files.
* @throws PathNotFoundException If a required file path is not found.
*/
private static function writeMakefileTemplate(ProjectManager $project_manager): void
{
$makefile_template = IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'Makefile.tpl');
$builds = [];
foreach($project_manager->getProjectConfiguration()->getBuild()->getBuildConfigurations() as $build_name)
{
$builds[$build_name] = str_replace('%TPL_BUILD_NAME%', $build_name, IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'make_build.tpl'));
}
$default_build = $project_manager->getProjectConfiguration()->getBuild()->getDefaultConfiguration();
$makefile_template = str_replace('%TPL_DEFAULT_BUILD_CONFIGURATION%', $default_build, $makefile_template);
$makefile_template = str_replace('%TPL_DEFAULT_BUILD_PATH%', $project_manager->getProjectConfiguration()->getBuild()->getBuildConfiguration($default_build)->getOutput(), $makefile_template);
$makefile_template = str_replace('%TPL_BUILD_NAMES%', implode(' ', array_keys($builds)), $makefile_template);
$build_template = '';
foreach($builds as $name => $template)
{
$build_template .= $template . PHP_EOL;
}
$makefile_template = str_replace('%TPL_BUILDS%', $build_template, $makefile_template);
IO::fwrite(
$project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'Makefile',
ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(), $makefile_template)
);
}
}

View file

@ -0,0 +1,93 @@
<?php
/*
* Copyright (c) Nosial 2022-2023, all rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including without
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
namespace ncc\Classes\PhpExtension\Templates;
use ncc\Classes\NccExtension\ConstantCompiler;
use ncc\Enums\Options\BuildConfigurationOptions;
use ncc\Enums\Options\ProjectOptions;
use ncc\Enums\Runners;
use ncc\Enums\SpecialConstants\AssemblyConstants;
use ncc\Enums\Types\BuildOutputType;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\PathNotFoundException;
use ncc\Interfaces\TemplateInterface;
use ncc\Managers\ProjectManager;
use ncc\Objects\ProjectConfiguration\Build\BuildConfiguration;
use ncc\Objects\ProjectConfiguration\ExecutionPolicy;
use ncc\Utilities\IO;
class PhpUnitTemplate implements TemplateInterface
{
/**
* Applies the necessary templates for the given project.
*
* @param ProjectManager $project_manager Manager responsible for handling project-related tasks.
*
* @return void
*/
public static function applyTemplate(ProjectManager $project_manager): void
{
self::createPhpUnitBootstrapTemplate($project_manager);
self::createPhpUnitTemplate($project_manager);
}
/**
* Creates a PHPUnit template in the specified project directory.
*
* @param ProjectManager $project_manager The project manager instance containing project configuration and path details.
* @return void
* @throws IOException
* @throws PathNotFoundException
*/
private static function createPhpUnitTemplate(ProjectManager $project_manager): void
{
IO::fwrite(
$project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'phpunit.xml',
ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(),
IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'phpunit.xml.tpl')
)
);
if(!file_exists($project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'tests'))
{
mkdir($project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'tests');
}
}
/**
* Creates the PHPUnit bootstrap template file for the given project.
*
* @param ProjectManager $project_manager The project manager instance handling project configuration and paths.
* @return void
*/
private static function createPhpUnitBootstrapTemplate(ProjectManager $project_manager): void
{
IO::fwrite(
$project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'bootstrap.php',
ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(),
IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php.tpl')
)
);
}
}

View file

@ -0,0 +1,3 @@
<?php
require 'ncc';
import('%ASSEMBLY.PACKAGE%');

View file

@ -0,0 +1,187 @@
name: CI
on:
push:
branches:
- '**'
release:
types: [created]
workflow_dispatch:
jobs:
%TPL_BUILDS%
# Checking for phpunit.xml
check-phpunit:
runs-on: ubuntu-latest
outputs:
phpunit-exists: ${{ steps.check.outputs.phpunit-exists }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check for phpunit.xml
id: check
run: |
if [ -f phpunit.xml ]; then
echo "phpunit-exists=true" >> $GITHUB_OUTPUT
else
echo "phpunit-exists=false" >> $GITHUB_OUTPUT
fi
# Checking for phpdoc.dist.xml
check-phpdoc:
runs-on: ubuntu-latest
outputs:
phpdoc-exists: ${{ steps.check.outputs.phpdoc-exists }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check for phpdoc.dist.xml
id: check
run: |
if [ -f phpdoc.dist.xml ]; then
echo "phpdoc-exists=true" >> $GITHUB_OUTPUT
else
echo "phpdoc-exists=false" >> $GITHUB_OUTPUT
fi
generate-phpdoc:
needs: [%TPL_DEFAULT_BUILD_CONFIGURATION%, check-phpdoc]
runs-on: ubuntu-latest
container:
image: php:8.3
if: needs.check-phpdoc.outputs.phpdoc-exists == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
apt update -yqq
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
- name: Download PHPDocumentor
run: |
wget https://phpdoc.org/phpDocumentor.phar
chmod +x phpDocumentor.phar
- name: Generate PHPDoc
run: |
php phpDocumentor.phar -d src -t docs
- name: Archive PHPDoc
run: |
zip -r docs.zip docs
- name: Upload PHPDoc
uses: actions/upload-artifact@v4
with:
name: documentation
path: docs.zip
test:
needs: [%TPL_BUILD_NAMES%, check-phpunit]
runs-on: ubuntu-latest
container:
image: php:8.3
if: needs.check-phpunit.outputs.phpunit-exists == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: %TPL_DEFAULT_BUILD_CONFIGURATION%
path: %TPL_DEFAULT_BUILD_CONFIGURATION%
- name: Install dependencies
run: |
apt update -yqq
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
chmod +x /usr/local/bin/install-php-extensions
install-php-extensions zip
- name: Install phive
run: |
wget -O phive.phar https://phar.io/releases/phive.phar
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
gpg --verify phive.phar.asc phive.phar
chmod +x phive.phar
mv phive.phar /usr/local/bin/phive
- name: Install phab
run: |
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
- name: Install latest version of NCC
run: |
git clone https://git.n64.cc/nosial/ncc.git
cd ncc
make redist
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
if [ -z "$NCC_DIR" ]; then
echo "NCC build directory not found"
exit 1
fi
php "$NCC_DIR/INSTALL" --auto
cd .. && rm -rf ncc
- name: Install NCC packages
run: |
ncc package install --package="%TPL_DEFAULT_ARTIFACT_BUILD_OUTPUT%" --build-source --reinstall -y --log-level debug
- name: Run PHPUnit tests
run: |
wget https://phar.phpunit.de/phpunit-11.3.phar
php phpunit-11.3.phar --configuration phpunit.xml --log-junit reports/junit.xml --log-teamcity reports/teamcity --testdox-html reports/testdox.html --testdox-text reports/testdox.txt
- name: Upload test reports
uses: actions/upload-artifact@v4
with:
name: reports
path: reports
release-documentation:
needs: generate-phpdoc
permissions: write-all
runs-on: ubuntu-latest
container:
image: php:8.3
if: github.event_name == 'release'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download documentation artifact
uses: actions/download-artifact@v4
with:
name: documentation
path: documentation
- name: Upload documentation artifact
uses: softprops/action-gh-release@v1
with:
files: |
documentation/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-artifacts:
needs: [%TPL_BUILD_NAMES%]
permissions: write-all
runs-on: ubuntu-latest
container:
image: php:8.3
if: github.event_name == 'release'
steps:
- name: Checkout repository
uses: actions/checkout@v4
%TPL_DOWNLOAD_ARTIFACTS%

View file

@ -0,0 +1,49 @@
%TPL_BUILD_NAME%:
runs-on: ubuntu-latest
container:
image: php:8.3
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
apt update -yqq
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
- name: Install phive
run: |
wget -O phive.phar https://phar.io/releases/phive.phar
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
gpg --verify phive.phar.asc phive.phar
chmod +x phive.phar
mv phive.phar /usr/local/bin/phive
- name: Install phab
run: |
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
- name: Install latest version of NCC
run: |
git clone https://git.n64.cc/nosial/ncc.git
cd ncc
make redist
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
if [ -z "$NCC_DIR" ]; then
echo "NCC build directory not found"
exit 1
fi
php "$NCC_DIR/INSTALL" --auto
cd .. && rm -rf ncc
- name: Build project
run: |
ncc build --config %TPL_BUILD_NAME% --build-source --log-level debug
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: %TPL_BUILD_NAME%
path: %TPL_BUILD_OUTPUT%

View file

@ -0,0 +1,12 @@
- name: Download %TPL_BUILD_NAME% artifact
uses: actions/download-artifact@v4
with:
name: %TPL_BUILD_NAME%
path: %TPL_BUILD_NAME%
- name: Upload %TPL_BUILD_NAME% artifact to release
uses: softprops/action-gh-release@v1
with:
files: |
%TPL_BUILD_NAME%/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -0,0 +1,2 @@
%TPL_BUILD_NAME%:
ncc build --config=%TPL_BUILD_NAME% --log-level $(LOG_LEVEL)

View file

@ -0,0 +1,11 @@
<phpunit bootstrap="bootstrap.php">
<testsuites>
<testsuite name="%ASSEMBLY.NAME% Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<php>
<ini name="error_reporting" value="-1"/>
<server name="KERNEL_DIR" value="app/"/>
</php>
</phpunit>

View file

@ -34,6 +34,7 @@
use ncc\Enums\Versions;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\ImportException;
use ncc\Exceptions\IntegrityException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\NotSupportedException;
use ncc\Exceptions\OperationException;
@ -77,12 +78,14 @@
* This method may exit the program without returning a value
*
* @param string $package
* @param array $arguments
* @return mixed
* @throws ConfigurationException
* @throws IOException
* @throws IntegrityException
* @throws NotSupportedException
* @throws PathNotFoundException
* @throws OperationException
* @throws PathNotFoundException
*/
public static function execute(string $package, array $arguments=[]): int
{
@ -93,6 +96,12 @@
if(self::$imported_packages[$package] instanceof PackageReader)
{
if(self::$imported_packages[$package]?->getMetadata()?->getMainExecutionPolicy() === null)
{
Console::out('The package does not have a main execution policy, skipping execution');
return 0;
}
return ExecutionUnitRunner::executeFromPackage(
self::$imported_packages[$package],
self::$imported_packages[$package]->getMetadata()->getMainExecutionPolicy()
@ -294,7 +303,7 @@
* @return string
* @throws ConfigurationException
* @throws ImportException
* @throws NotSupportedException
* @throws IntegrityException
* @throws OperationException
*/
private static function importFromPackage(string $package_path): string
@ -513,6 +522,7 @@
* @throws IOException
* @throws OperationException
* @throws PathNotFoundException
* @throws IntegrityException
*/
private static function acquireFile(string $path, ?string $package=null): string
{
@ -629,9 +639,16 @@
return;
}
if(!in_array($path, self::$included_files, true))
$acquired_name = $path;
if(!is_file($path))
{
self::$included_files[] = $path;
$acquired_name = hash('crc32', $acquired_file);
}
if(!in_array($acquired_name, self::$included_files, true))
{
self::$included_files[] = sprintf('virtual(%s)', $acquired_name);
}
self::extendedEvaluate($acquired_file);
@ -674,9 +691,16 @@
throw new RuntimeException(sprintf('Failed to acquire file "%s" at runtime: %s', $path, $e->getMessage()), $e->getCode(), $e);
}
if(!in_array($path, self::$included_files, true))
$acquired_name = $path;
if(!is_file($path))
{
self::$included_files[] = $path;
$acquired_name = hash('crc32', $acquired_file);
}
if(!in_array($acquired_name, self::$included_files, true))
{
self::$included_files[] = sprintf('virtual(%s)', $acquired_name);
}
self::extendedEvaluate($acquired_file);

View file

@ -33,4 +33,70 @@
* A template that is used to create a PHP CLI application project
*/
case PHP_CLI = 'phpcli';
/**
* A template for generating a Makefile for the PHP project
*/
case PHP_MAKE = 'phpmake';
/**
* A template used for creating PHP Unit testing bootstrap
*/
case PHP_UNIT = 'phpunit';
/**
* A template that is used to create a PHP executable project
*/
case PHP_COMPRESSED = 'phpgz';
/**
* A template that is used to create a PHP executable project
*/
case PHP_EXECUTABLE = 'phpexe';
/**
* Template that combines PHP_LIBRARY, PHP_MAKE and PHP_UNIT in one
*/
case PHP_LIBRARY_FULL = 'phplib_full';
/**
* Template that combines PHP_LIBRARY, PHP_MAKE, PHP_UNIT and PHP_CLI in one
*/
case PHP_CLI_FULL = 'phpcli_full';
/**
* Template that applies a GitHub workflow CI that builds the project, tests the project and creates
* automatic builds for releases
*/
case PHP_GITHUB_CI = 'phpci_github';
/**
* Suggests the closest matching `ProjectTemplates` instance based on the given input string.
*
* @param string $input The input string to compare against.
* @return ProjectTemplates|null The closest matching `ProjectTemplates` instance, or null if no close match is found.
*/
public static function suggest(string $input): ?ProjectTemplates
{
$closest = null;
$shortest_distance = -1;
foreach (self::cases() as $case)
{
$distance = levenshtein($input, $case->value);
if ($distance === 0)
{
return $case;
}
if ($shortest_distance === -1 || $distance < $shortest_distance)
{
$closest = $case;
$shortest_distance = $distance;
}
}
return $closest ?: null;
}
}

View file

@ -0,0 +1,37 @@
<?php
/*
* Copyright (c) Nosial 2022-2024, all rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including without
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
namespace ncc\Enums\SpecialConstants;
enum GeneralConstants : string
{
/**
* The project's default build configuration
*/
case DEFAULT_BUILD_CONFIGURATION = '%DEFAULT_BUILD_CONFIGURATION%';
/**
* Configurable build output path
*/
case BUILD_OUTPUT_PATH = '%BUILD_OUTPUT_PATH%';
}

View file

@ -28,6 +28,7 @@
use Exception;
use InvalidArgumentException;
use ncc\Classes\ArchiveExtractor;
use ncc\Classes\ExecutionUnitRunner;
use ncc\Classes\PackageReader;
use ncc\Classes\ShutdownHandler;
use ncc\CLI\Main;
@ -44,6 +45,7 @@
use ncc\Enums\Types\ProjectType;
use ncc\Enums\Versions;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IntegrityException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\NetworkException;
use ncc\Exceptions\NotSupportedException;
@ -112,6 +114,7 @@
* @param PackageReader $package_reader
* @return Dependency[]
* @throws ConfigurationException
* @throws IntegrityException
*/
public function checkRequiredDependencies(PackageReader $package_reader): array
{
@ -395,6 +398,7 @@
* @return array
* @throws ConfigurationException
* @throws IOException
* @throws IntegrityException
* @throws OperationException
* @throws PathNotFoundException
*/
@ -423,6 +427,20 @@
$this->uninstall($package_reader->getAssembly()->getPackage(), $package_reader->getAssembly()->getVersion());
}
if($package_reader->getInstaller() !== null && count($package_reader->getInstaller()->getPreInstall()) > 0)
{
foreach ($package_reader->getInstaller()->getPreInstall() as $unit)
{
Console::outVerbose(sprintf('Running pre-install unit: %s', $unit));
if(!$package_reader->executionUnitExists($unit))
{
throw new OperationException(sprintf("Unable to run pre-install unit '%s' because it is not defined in the package", $unit));
}
ExecutionUnitRunner::executeFromPackage($package_reader, $unit);
}
}
$filesystem = new Filesystem();
$package_path = PathFinder::getPackagesPath() . DIRECTORY_SEPARATOR . sprintf(
'%s=%s', $package_reader->getAssembly()->getPackage(), $package_reader->getAssembly()->getVersion()
@ -525,6 +543,20 @@
}
}
if($package_reader->getInstaller() !== null && count($package_reader->getInstaller()->getPostInstall()) > 0)
{
foreach ($package_reader->getInstaller()->getPostInstall() as $unit)
{
Console::outVerbose(sprintf('Running post-install unit: %s', $unit));
if(!$package_reader->executionUnitExists($unit))
{
throw new OperationException(sprintf("Unable to run post-install unit '%s' because it is not defined in the package", $unit));
}
ExecutionUnitRunner::executeFromPackage($package_reader, $unit);
}
}
return $installed_packages;
}
@ -654,8 +686,8 @@
* @return void
* @throws ConfigurationException
* @throws IOException
* @throws NotSupportedException
* @throws OperationException
* @throws IntegrityException
*/
private function extractPackageContents(PackageReader $package_reader, string $package_path): void
{

View file

@ -29,9 +29,13 @@
use ncc\Classes\PhpExtension\ExecutableCompiler;
use ncc\Classes\PhpExtension\NccCompiler;
use ncc\Classes\PhpExtension\Templates\CliTemplate;
use ncc\Classes\PhpExtension\Templates\CompressedTemplate;
use ncc\Classes\PhpExtension\Templates\ExecutableTemplate;
use ncc\Classes\PhpExtension\Templates\GitHubWorkflowTemplate;
use ncc\Classes\PhpExtension\Templates\LibraryTemplate;
use ncc\Classes\PhpExtension\Templates\MakefileTemplate;
use ncc\Classes\PhpExtension\Templates\PhpUnitTemplate;
use ncc\Enums\CompilerExtensions;
use ncc\Enums\ComponentFileExtensions;
use ncc\Enums\Options\BuildConfigurationOptions;
use ncc\Enums\Options\BuildConfigurationValues;
use ncc\Enums\Options\InitializeProjectOptions;
@ -189,17 +193,59 @@
*/
public function applyTemplate(string $template_name): void
{
switch(strtolower($template_name))
switch(ProjectTemplates::tryFrom(strtolower($template_name)))
{
case ProjectTemplates::PHP_CLI->value:
case ProjectTemplates::PHP_CLI:
CliTemplate::applyTemplate($this);
break;
case ProjectTemplates::PHP_LIBRARY->value:
case ProjectTemplates::PHP_LIBRARY:
LibraryTemplate::applyTemplate($this);
break;
case ProjectTemplates::PHP_MAKE:
MakefileTemplate::applyTemplate($this);
break;
case ProjectTemplates::PHP_UNIT:
PhpUnitTemplate::applyTemplate($this);
break;
case ProjectTemplates::PHP_COMPRESSED:
CompressedTemplate::applyTemplate($this);
break;
case ProjectTemplates::PHP_EXECUTABLE:
ExecutableTemplate::applyTemplate($this);
break;
case ProjectTemplates::PHP_LIBRARY_FULL:
LibraryTemplate::applyTemplate($this);
MakefileTemplate::applyTemplate($this);
PhpUnitTemplate::applyTemplate($this);
CompressedTemplate::applyTemplate($this);
break;
case ProjectTemplates::PHP_CLI_FULL:
CliTemplate::applyTemplate($this);
LibraryTemplate::applyTemplate($this);
MakefileTemplate::applyTemplate($this);
PhpUnitTemplate::applyTemplate($this);
CompressedTemplate::applyTemplate($this);
ExecutableTemplate::applyTemplate($this);
break;
case ProjectTemplates::PHP_GITHUB_CI:
GitHubWorkflowTemplate::applyTemplate($this);
break;
default:
$suggestion = ProjectTemplates::suggest($template_name);
if($suggestion !== null)
{
throw new NotSupportedException('The given template \'' . $template_name . '\' is not supported, did you mean \'' . $suggestion->value . '\'?');
}
throw new NotSupportedException('The given template \'' . $template_name . '\' is not supported');
}
}
@ -491,6 +537,7 @@
// Create the build configuration
$build = new ProjectConfiguration\Build('auto_src');
$build->setDefaultConfiguration('release_ncc');
$require_dev = [];
// Process dependencies
if($composer_json->getRequire() !== null)
@ -511,7 +558,6 @@
}
// Process developer dependencies
$require_dev = [];
if($composer_json->getRequireDev() !== null)
{
/** @var ComposerJson\PackageLink $package_link */
@ -523,10 +569,9 @@
}
$source = sprintf('%s=%s@packagist', $package_link->getPackageName(), $package_link->getVersion());
$build->addDependency(new ProjectConfiguration\Dependency(
$require_dev[] = new ProjectConfiguration\Dependency(
Resolver::composerNameToPackage($package_link->getPackageName()), $source, $package_link->getVersion()
));
$require_dev[] = $package_link->getPackageName();
);
}
}

View file

@ -24,6 +24,7 @@
namespace ncc\Objects\PackageLock;
use Exception;
use InvalidArgumentException;
use ncc\Classes\PackageReader;
use ncc\Enums\FileDescriptor;
@ -41,6 +42,7 @@
use ncc\Objects\ProjectConfiguration\UpdateSource;
use ncc\ThirdParty\composer\semver\Semver;
use ncc\ThirdParty\jelix\Version\VersionComparator;
use ncc\Utilities\Console;
use ncc\Utilities\Functions;
use ncc\Utilities\IO;
@ -189,8 +191,9 @@
{
$version = $this->getLatestVersion();
}
catch(InvalidArgumentException $e)
catch(Exception $e)
{
Console::outWarning(sprintf('Unable to find latest version for package %s, called on versionExists() in PackageEntry: %s', $this->name, $e->getMessage()));
return false;
}
}

View file

@ -500,6 +500,25 @@
throw new InvalidArgumentException(sprintf('The build configuration "%s" does not exist', $name));
}
/**
* Checks if a build configuration exists
*
* @param string $name The name of the build configuration to check
* @return bool Returns true if the build configuration exists, false otherwise
*/
public function buildConfigurationExists(string $name): bool
{
try
{
$this->getBuildConfiguration($name);
return true;
}
catch(InvalidArgumentException)
{
return false;
}
}
/**
* @param array $build_configurations
* @return void

View file

@ -84,6 +84,64 @@
$this->post_update = [];
}
/**
* Retrieves the pre-installation configuration.
*
* @return array The pre-installation configuration settings.
*/
public function getPreInstall(): array
{
return $this->pre_install;
}
/**
*
* @return array Returns the post-installation steps.
*/
public function getPostInstall(): array
{
return $this->post_install;
}
/**
* Retrieves the list of pre-uninstall tasks.
*
* @return array The array containing pre-uninstall tasks.
*/
public function getPreUninstall(): array
{
return $this->pre_uninstall;
}
/**
* Retrieves the post-uninstall steps array.
*
* @return array An array containing the post-uninstall steps.
*/
public function getPostUninstall(): array
{
return $this->post_uninstall;
}
/**
*
* @return array The pre-update data array.
*/
public function getPreUpdate(): array
{
return $this->pre_update;
}
/**
* Retrieves the list of post-update actions.
*
* @return array An array containing post-update actions.
*/
public function getPostUpdate(): array
{
return $this->post_update;
}
/**
* @inheritDoc
*/

View file

@ -25,6 +25,9 @@
namespace ncc\Utilities;
use ncc\CLI\Main;
use ncc\Enums\LogLevel;
class ConsoleProgressBar
{
/**
@ -286,6 +289,11 @@
*/
public function update(): void
{
if(LogLevel::VERBOSE->checkLogLevel(Main::getLogLevel()))
{
return;
}
if($this->auto_end && $this->value >= $this->max_value)
{
print($this->renderInformation() . $this->renderProgressBar() . "\n");
@ -368,10 +376,16 @@
*/
private function renderProgressBar(): string
{
$hashes_count = round($this->progress_width * $this->value / $this->max_value);
$dashes_count = $this->progress_width - $hashes_count;
$percent_done = round($this->value * 100 / $this->max_value);
$hashes_count = 0;
$percent_done = 0;
if($this->max_value !== 0)
{
$hashes_count = round($this->progress_width * $this->value / $this->max_value);
$percent_done = round($this->value * 100 / $this->max_value);
}
$dashes_count = $this->progress_width - $hashes_count;
return ' [' . str_repeat('#', $hashes_count) . str_repeat('-', $dashes_count) . ']' . sprintf('%4s', $percent_done) . '%';
}

View file

@ -41,6 +41,11 @@
*/
private static $user_id_cache;
/**
* @var array
*/
private static $resolved_hosts = [];
/**
* Returns the current scope of the application
*
@ -299,4 +304,54 @@
return explode(':', $component_path, 2)[1];
}
/**
* Resolve host and cache the result
*
* @param string $host The host to resolve
* @return string The resolved host IP address
*/
public static function resolveHost(string $host): string
{
$resolved_host = self::$resolved_hosts[$host] ?? null;
if($resolved_host !== null)
{
Console::outDebug(sprintf('Resolved host "%s" to "%s" from cache', $host, $resolved_host));
return $resolved_host;
}
$resolved_host = gethostbyname($host);
if($resolved_host === $host)
{
Console::outDebug(sprintf('Unable to resolve host "%s"', $host));
return $host;
}
Console::outDebug(sprintf('Resolved host "%s" to "%s"', $host, $resolved_host));
self::$resolved_hosts[$host] = $resolved_host;
return $resolved_host;
}
/**
* Get resolved option for the given URL
*
* @param string $url The input URL
* @return string|null Resolved option in the format "{host}:{port}:{ip_address}", or null if $ip_address is same as $host
*/
public static function getResolveOption(string $url): ?string
{
$parsed_url = parse_url($url);
$host = $parsed_url['host'];
$port = str_starts_with($url, 'https') ? 443 : 80;
$ip_address = self::resolveHost($host);
if($ip_address === $host)
{
return null;
}
return "{$host}:{$port}:{$ip_address}";
}
}

View file

@ -1 +1 @@
2.1.0
2.1.6

View file

@ -1,5 +1,5 @@
{
"version": "2.1.0",
"version": "2.1.6",
"branch": "stable",
"flags": [],
"components": [