- Refactored the entire package structure to ncc package structure 2.0 for memory efficiency and performance

This commit is contained in:
Netkas 2023-09-04 21:56:07 -04:00
parent d4f69522fc
commit d41ae8114f
No known key found for this signature in database
GPG key ID: 5DAF58535614062B
28 changed files with 1574 additions and 1399 deletions

5
.idea/php.xml generated
View file

@ -17,6 +17,11 @@
<component name="PhpProjectSharedConfiguration" php_language_level="8.2"> <component name="PhpProjectSharedConfiguration" php_language_level="8.2">
<option name="suggestChangeDefaultLanguageLevel" value="false" /> <option name="suggestChangeDefaultLanguageLevel" value="false" />
</component> </component>
<component name="PhpRuntimeConfiguration">
<extensions>
<extension name="msgpack" enabled="true" />
</extensions>
</component>
<component name="PhpStanOptionsConfiguration"> <component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" /> <option name="transferred" value="true" />
</component> </component>

View file

@ -226,6 +226,7 @@ features and reduced the number of exceptions down to 15 exceptions.
ExitHandler, Repository, Assembly, Build, Dependency, ExecutionPolicy, Installer, Project, UpdateSource) I'm not ExitHandler, Repository, Assembly, Build, Dependency, ExecutionPolicy, Installer, Project, UpdateSource) I'm not
going to list them all here, but you can find them in the commit history. going to list them all here, but you can find them in the commit history.
- Implemented a template engine and refactored the CLI menu for the Project Manager and added a new `template` command - Implemented a template engine and refactored the CLI menu for the Project Manager and added a new `template` command
- Refactored the entire package structure to ncc package structure 2.0 for memory efficiency and performance
### Removed ### Removed

View file

@ -1,5 +1,5 @@
<?php <?php
/* /*
* Copyright (c) Nosial 2022-2023, all rights reserved. * 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 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
@ -20,7 +20,7 @@
* *
*/ */
namespace ncc\CLI\Commands; namespace ncc\CLI\Commands;
use Exception; use Exception;
use ncc\Enums\Options\BuildConfigurationValues; use ncc\Enums\Options\BuildConfigurationValues;
@ -58,33 +58,28 @@ namespace ncc\CLI\Commands;
*/ */
private static function build($args): void private static function build($args): void
{ {
// Determine the path of the project
if(isset($args['path']) || isset($args['p'])) if(isset($args['path']) || isset($args['p']))
{ {
$path_arg = ($args['path'] ?? $args['p']); $project_path = $args['path'] ?? $args['p'];
// Check if the path exists
if(!file_exists($path_arg) || !is_dir($path_arg))
{
Console::outError('The given path \'' . $path_arg . '\' is does not exist or is not a directory', true, 1);
} }
elseif(is_file(getcwd() . DIRECTORY_SEPARATOR . 'project.json'))
$project_path = $path_arg; {
$project_path = getcwd();
} }
else else
{ {
$project_path = getcwd(); Console::outError('Missing option: --path|-p, please specify the path to the project', true, 1);
return;
} }
// Load the project // Load the project
try try
{ {
$ProjectManager = new ProjectManager($project_path); $project_manager = new ProjectManager($project_path);
$ProjectManager->load();
} }
catch (Exception $e) catch (Exception $e)
{ {
Console::outException('Failed to load Project Configuration (project.json)', $e, 1); Console::outException('There was an error loading the project', $e, 1);
return; return;
} }
@ -97,10 +92,7 @@ namespace ncc\CLI\Commands;
$build_configuration = $args['config']; $build_configuration = $args['config'];
} }
$output = $ProjectManager->build($build_configuration); $output = $project_manager->build($build_configuration);
Console::out('Successfully built ' . $output);
exit(0);
} }
catch (Exception $e) catch (Exception $e)
{ {
@ -108,6 +100,7 @@ namespace ncc\CLI\Commands;
return; return;
} }
Console::out($output);
} }
/** /**

View file

@ -178,7 +178,7 @@
try try
{ {
Console::out('magic_bytes: ' . json_encode(($package->getMagicBytes()?->toArray() ?? []), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); Console::out('magic_bytes: ' . json_encode(($package->getMagicBytes()?->toArray() ?? []), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
Console::out('header: ' . json_encode(($package->getHeader()?->toArray() ?? []), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); Console::out('header: ' . json_encode(($package->getMetadata()?->toArray() ?? []), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
Console::out('assembly: ' . json_encode(($package->getAssembly()?->toArray() ?? []), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); Console::out('assembly: ' . json_encode(($package->getAssembly()?->toArray() ?? []), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
Console::out('installer: ' . json_encode(($package->getInstaller()?->toArray() ?? []), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); Console::out('installer: ' . json_encode(($package->getInstaller()?->toArray() ?? []), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
} }
@ -559,15 +559,15 @@
} }
Console::out(sprintf('Extension: %s', Console::out(sprintf('Extension: %s',
Console::formatColor($package->getHeader()->getCompilerExtension()->getExtension(), ConsoleColors::GREEN) Console::formatColor($package->getMetadata()->getCompilerExtension()->getExtension(), ConsoleColors::GREEN)
)); ));
Console::out(sprintf('Maximum Version: %s', Console::out(sprintf('Maximum Version: %s',
Console::formatColor($package->getHeader()->getCompilerExtension()->getMinimumVersion(), ConsoleColors::LIGHT_MAGENTA) Console::formatColor($package->getMetadata()->getCompilerExtension()->getMinimumVersion(), ConsoleColors::LIGHT_MAGENTA)
)); ));
Console::out(sprintf('Minimum Version: %s', Console::out(sprintf('Minimum Version: %s',
Console::formatColor($package->getHeader()->getCompilerExtension()->getMinimumVersion(), ConsoleColors::LIGHT_MAGENTA) Console::formatColor($package->getMetadata()->getCompilerExtension()->getMinimumVersion(), ConsoleColors::LIGHT_MAGENTA)
)); ));
if(!$user_confirmation) if(!$user_confirmation)

View file

@ -0,0 +1,188 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
/*
* 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\NccExtension;
use ncc\Classes\PackageWriter;
use ncc\CLI\Main;
use ncc\Enums\ComponentDataType;
use ncc\Enums\LogLevel;
use ncc\Enums\Options\BuildConfigurationValues;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\NotSupportedException;
use ncc\Exceptions\PathNotFoundException;
use ncc\Managers\ProjectManager;
use ncc\Objects\Package;
use ncc\Objects\Package\Resource;
use ncc\Utilities\Base64;
use ncc\Utilities\Console;
use ncc\Utilities\Functions;
use ncc\Utilities\IO;
use ncc\Utilities\Resolver;
class NccCompiler
{
/**
* @var ProjectManager
*/
private $project_manager;
/**
* @param ProjectManager $project_manager
*/
public function __construct(ProjectManager $project_manager)
{
$this->project_manager = $project_manager;
}
/**
* @param string $build_configuration
* @return string
* @throws ConfigurationException
* @throws IOException
* @throws NotSupportedException
* @throws PathNotFoundException
*/
public function build(string $build_configuration=BuildConfigurationValues::DEFAULT): string
{
$configuration = $this->project_manager->getProjectConfiguration()->getBuild()->getBuildConfiguration($build_configuration);
$package_path = $configuration->getOutputPath() . DIRECTORY_SEPARATOR . $this->project_manager->getProjectConfiguration()->getAssembly()->getPackage() . '.ncc';
$package_writer = new PackageWriter($package_path);
Console::out(sprintf('Building project \'%s\'', $this->project_manager->getProjectConfiguration()->getAssembly()->getName()));
// Debugging information
if(Resolver::checkLogLevel(LogLevel::DEBUG, Main::getLogLevel()))
{
foreach($this->project_manager->getProjectConfiguration()->getAssembly()->toArray() as $prop => $value)
{
Console::outDebug(sprintf('assembly.%s: %s', $prop, ($value ?? 'n/a')));
}
foreach($this->project_manager->getProjectConfiguration()->getProject()->getCompiler()->toArray() as $prop => $value)
{
Console::outDebug(sprintf('compiler.%s: %s', $prop, ($value ?? 'n/a')));
}
}
Console::outVerbose('Building package header...');
$package_writer->setMetadata($this->buildMetadata($build_configuration));
Console::outVerbose('Adding assembly information...');
$package_writer->setAssembly($this->project_manager->getProjectConfiguration()->getAssembly());
if($this->project_manager->getProjectConfiguration()->getInstaller() !== null)
{
Console::outVerbose('Adding installer information...');
/** @noinspection NullPointerExceptionInspection */
$package_writer->setInstaller($this->project_manager->getProjectConfiguration()->getInstaller());
}
// Process execution policies
if(count($this->project_manager->getProjectConfiguration()->getExecutionPolicies()) > 0)
{
Console::out('Processing execution policies...');
$execution_units = $this->project_manager->getExecutionUnits($build_configuration);
if(count($execution_units) === 0)
{
Console::outWarning('The project contains execution policies but none of them are used');
}
foreach($execution_units as $unit)
{
$package_writer->addExecutionUnit($unit);
}
}
// Compile package components
foreach($this->project_manager->getComponents($build_configuration) as $component)
{
Console::outVerbose(sprintf('Compiling \'%s\'', $component));
$package_writer->addComponent($this->buildComponent($component));
}
// Compile package resources
foreach($this->project_manager->getResources($build_configuration) as $resource)
{
Console::outVerbose(sprintf('Processing \'%s\'', $resource));
$package_writer->addResource($this->buildResource($resource));
}
$package_writer->close();
return $package_path;
}
/**
* Compiles a single component as a base64 encoded string
*
* @param string $file_path
* @return Package\Component
* @throws IOException
* @throws PathNotFoundException
*/
public function buildComponent(string $file_path): Package\Component
{
return new Package\Component(
Functions::removeBasename($file_path),
Base64::encode(IO::fread($file_path)), ComponentDataType::BASE64_ENCODED
);
}
/**
* @param string $file_path
* @return Resource
* @throws IOException
* @throws PathNotFoundException
*/
public function buildResource(string $file_path): Package\Resource
{
return new Package\Resource(
basename($file_path), IO::fread($file_path)
);
}
/**
* Builds the package header
*
* @param ProjectManager $project_manager
* @param string $build_configuration
* @return Package\Metadata
* @throws ConfigurationException
*/
public function buildMetadata(string $build_configuration=BuildConfigurationValues::DEFAULT): Package\Metadata
{
$header = new Package\Metadata($this->project_manager->getProjectConfiguration()->getProject()->getCompiler());
$header->setRuntimeConstants($this->project_manager->getRuntimeConstants($build_configuration));
$header->setOptions($this->project_manager->getCompilerOptions($build_configuration));
$header->setUpdateSource($this->project_manager->getProjectConfiguration()->getProject()->getUpdateSource());
$header->setMainExecutionPolicy($this->project_manager->getProjectConfiguration()->getBuild()->getMain());
$header->setInstaller($this->project_manager->getProjectConfiguration()->getInstaller());
return $header;
}
}

View file

@ -1,405 +0,0 @@
<?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\NccExtension;
use Exception;
use ncc\Enums\CompilerExtensions;
use ncc\Enums\ConstantReferences;
use ncc\Enums\LogLevel;
use ncc\Enums\Options\BuildConfigurationValues;
use ncc\Enums\ProjectType;
use ncc\Classes\ComposerExtension\ComposerSourceBuiltin;
use ncc\Classes\PhpExtension\PhpCompiler;
use ncc\CLI\Main;
use ncc\Exceptions\BuildException;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\NotSupportedException;
use ncc\Exceptions\PathNotFoundException;
use ncc\Interfaces\CompilerInterface;
use ncc\Managers\ProjectManager;
use ncc\ncc;
use ncc\Objects\Package;
use ncc\Objects\ProjectConfiguration;
use ncc\Objects\ProjectConfiguration\Assembly;
use ncc\ThirdParty\Symfony\Filesystem\Filesystem;
use ncc\Utilities\Console;
use ncc\Utilities\Functions;
use ncc\Utilities\Resolver;
class PackageCompiler
{
/**
* Compiles the project into a package
*
* @param ProjectManager $manager
* @param string $build_configuration
* @return string
* @throws BuildException
* @throws ConfigurationException
* @throws IOException
* @throws NotSupportedException
* @throws PathNotFoundException
*/
public static function compile(ProjectManager $manager, string $build_configuration=BuildConfigurationValues::DEFAULT): string
{
$configuration = $manager->getProjectConfiguration();
if(Resolver::checkLogLevel(LogLevel::DEBUG, Main::getLogLevel()))
{
foreach($configuration->getAssembly()->toArray() as $prop => $value)
{
Console::outDebug(sprintf('assembly.%s: %s', $prop, ($value ?? 'n/a')));
}
foreach($configuration->getProject()->getCompiler()->toArray() as $prop => $value)
{
Console::outDebug(sprintf('compiler.%s: %s', $prop, ($value ?? 'n/a')));
}
}
// Select the correct compiler for the specified extension
if (strtolower($configuration->getProject()->getCompiler()->getExtension()) === CompilerExtensions::PHP)
{
/** @var CompilerInterface $Compiler */
$Compiler = new PhpCompiler($configuration, $manager->getProjectPath());
}
else
{
throw new NotSupportedException('The compiler extension \'' . $configuration->getProject()->getCompiler()->getExtension() . '\' is not supported');
}
$build_configuration = $configuration->getBuild()->getBuildConfiguration($build_configuration)->getName();
Console::out(sprintf('Building %s=%s', $configuration->getAssembly()->getPackage(), $configuration->getAssembly()->getVersion()));
$Compiler->prepare($build_configuration);
$Compiler->build();
return self::writePackage(
$manager->getProjectPath(), $Compiler->getPackage(), $configuration, $build_configuration
);
}
/**
* Attempts to detect the project type and convert it accordingly before compiling
* Returns the compiled package path
*
* @param string $path
* @param string|null $version
* @return string
* @throws BuildException
*/
public static function tryCompile(string $path, ?string $version=null): string
{
$project_type = Resolver::detectProjectType($path);
try
{
if($project_type->getProjectType() === ProjectType::COMPOSER)
{
$project_path = ComposerSourceBuiltin::fromLocal($project_type->getProjectPath());
}
elseif($project_type->getProjectType() === ProjectType::NCC)
{
$project_manager = new ProjectManager($project_type->getProjectPath());
$project_manager->getProjectConfiguration()->getAssembly()->setVersion($version);
$project_path = $project_manager->build();
}
else
{
throw new NotSupportedException(sprintf('Failed to compile %s, project type %s is not supported', $project_type->getProjectPath(), $project_type->getProjectType()));
}
if($version !== null)
{
$package = Package::load($project_path);
$package->getAssembly()->setVersion(Functions::convertToSemVer($version));
$package->save($project_path);
}
return $project_path;
}
catch(Exception $e)
{
throw new BuildException('Failed to build project', $e);
}
}
/**
* Compiles the execution policies of the package
*
* @param string $path
* @param ProjectConfiguration $configuration
* @return array
* @throws IOException
* @throws NotSupportedException
* @throws PathNotFoundException
*/
public static function compileExecutionPolicies(string $path, ProjectConfiguration $configuration): array
{
if(count($configuration->getExecutionPolicies()) === 0)
{
return [];
}
Console::out('Compiling Execution Policies');
$total_items = count($configuration->getExecutionPolicies());
$execution_units = [];
$processed_items = 1;
/** @var ProjectConfiguration\ExecutionPolicy $policy */
foreach($configuration->getExecutionPolicies() as $policy)
{
Console::outVerbose(sprintf('Compiling Execution Policy %s', $policy->getName()));
/** @noinspection DisconnectedForeachInstructionInspection */
if($total_items > 5)
{
Console::inlineProgressBar($processed_items, $total_items);
}
$unit_path = Functions::correctDirectorySeparator($path . $policy->getExecute()->getTarget());
$execution_units[] = Functions::compileRunner($unit_path, $policy);
}
if($total_items > 5 && ncc::cliMode())
{
print(PHP_EOL);
}
return $execution_units;
}
/**
* Writes the finished package to disk, returns the output path
*
* @param string $path
* @param Package $package
* @param ProjectConfiguration $configuration
* @param string $build_configuration
* @return string
* @throws IOException
* @throws ConfigurationException
*/
public static function writePackage(string $path, Package $package, ProjectConfiguration $configuration, string $build_configuration=BuildConfigurationValues::DEFAULT): string
{
Console::outVerbose(sprintf('Writing package to %s', $path));
// Write the package to disk
$FileSystem = new Filesystem();
$BuildConfiguration = $configuration->getBuild()->getBuildConfiguration($build_configuration);
if(!$FileSystem->exists($path . $BuildConfiguration->getOutputPath()))
{
Console::outDebug(sprintf('creating output directory %s', $path . $BuildConfiguration->getOutputPath()));
$FileSystem->mkdir($path . $BuildConfiguration->getOutputPath());
}
// Finally write the package to the disk
$FileSystem->mkdir($path . $BuildConfiguration->getOutputPath());
$output_file = $path . $BuildConfiguration->getOutputPath() . DIRECTORY_SEPARATOR . $package->getAssembly()->getPackage() . '.ncc';
if($FileSystem->exists($output_file))
{
Console::outDebug(sprintf('removing existing package %s', $output_file));
$FileSystem->remove($output_file);
}
$FileSystem->touch($output_file);
try
{
$package->save($output_file);
}
catch(Exception $e)
{
throw new IOException('Cannot write to output file', $e);
}
return $output_file;
}
/**
* Compiles the constants in the package object
*
* @param Package $package
* @param array $refs
* @return void
*/
public static function compilePackageConstants(Package $package, array $refs): void
{
if($package->getAssembly() !== null)
{
$assembly = [];
foreach($package->getAssembly()->toArray() as $key => $value)
{
Console::outDebug(sprintf('compiling constant Assembly.%s (%s)', $key, implode(', ', array_keys($refs))));
$assembly[$key] = self::compileConstants($value, $refs);
}
$package->setAssembly(Assembly::fromArray($assembly));
unset($assembly);
}
if(count($package->getExecutionUnits()) > 0)
{
$units = [];
foreach($package->ExecutionUnits() as $executionUnit)
{
Console::outDebug(sprintf('compiling execution unit constant %s (%s)', $executionUnit->getExecutionPolicy()->getName(), implode(', ', array_keys($refs))));
$units[] = self::compileExecutionUnitConstants($executionUnit, $refs);
}
$package->setExecutionUnits($units);
unset($units);
}
$compiled_constants = [];
foreach($package->getHeader()->getRuntimeConstants() as $name => $value)
{
Console::outDebug(sprintf('compiling runtime constant %s (%s)', $name, implode(', ', array_keys($refs))));
$compiled_constants[$name] = self::compileConstants($value, $refs);
}
$options = [];
foreach($package->getHeader()->getOptions() as $name => $value)
{
if(is_array($value))
{
$options[$name] = [];
foreach($value as $key => $val)
{
if(!is_string($val))
{
continue;
}
Console::outDebug(sprintf('compiling option %s.%s (%s)', $name, $key, implode(', ', array_keys($refs))));
$options[$name][$key] = self::compileConstants($val, $refs);
}
}
else
{
Console::outDebug(sprintf('compiling option %s (%s)', $name, implode(', ', array_keys($refs))));
$options[$name] = self::compileConstants((string)$value, $refs);
}
}
$package->getHeader()->setOptions($options);
$package->getHeader()->setRuntimeConstants($compiled_constants);
}
/**
* Compiles the constants in a given execution unit
*
* @param Package\ExecutionUnit $unit
* @param array $refs
* @return Package\ExecutionUnit
*/
public static function compileExecutionUnitConstants(Package\ExecutionUnit $unit, array $refs): Package\ExecutionUnit
{
$unit->getExecutionPolicy()->setMessage(self::compileConstants($unit->getExecutionPolicy()->getMessage(), $refs));
if($unit->getExecutionPolicy()->getExitHandlers() !== null)
{
if($unit->getExecutionPolicy()->getExitHandlers()->getSuccess()?->getMessage() !== null)
{
$unit->getExecutionPolicy()->getExitHandlers()->getSuccess()?->setMessage(
self::compileConstants($unit->getExecutionPolicy()->getExitHandlers()->getSuccess()->getMessage(), $refs)
);
}
if($unit->getExecutionPolicy()->getExitHandlers()->getError()?->getMessage() !== null)
{
$unit->getExecutionPolicy()->getExitHandlers()->getError()?->setMessage(
self::compileConstants($unit->getExecutionPolicy()->getExitHandlers()->getError()->getMessage(), $refs)
);
}
if($unit->getExecutionPolicy()->getExitHandlers()->getWarning()?->getMessage() !== null)
{
$unit->getExecutionPolicy()->getExitHandlers()->getWarning()?->setMessage(
self::compileConstants($unit->getExecutionPolicy()->getExitHandlers()->getWarning()->getMessage(), $refs)
);
}
}
if($unit->getExecutionPolicy()->getExecute() !== null)
{
$unit->getExecutionPolicy()->getExecute()->setTarget(self::compileConstants($unit->getExecutionPolicy()->getExecute()->getTarget(), $refs));
$unit->getExecutionPolicy()->getExecute()->setWorkingDirectory(self::compileConstants($unit->getExecutionPolicy()->getExecute()->getWorkingDirectory(), $refs));
if(count($unit->getExecutionPolicy()->getExecute()->getOptions()) > 0)
{
$options = [];
foreach($unit->getExecutionPolicy()->getExecute()->getOptions() as $key=> $value)
{
$options[self::compileConstants($key, $refs)] = self::compileConstants($value, $refs);
}
$unit->getExecutionPolicy()->getExecute()->setOptions($options);
}
}
return $unit;
}
/**
* Compiles multiple types of constants
*
* @param string|null $value
* @param array $refs
* @return string|null
*/
public static function compileConstants(?string $value, array $refs): ?string
{
if($value === null)
{
return null;
}
if(isset($refs[ConstantReferences::ASSEMBLY]))
{
$value = ConstantCompiler::compileAssemblyConstants($value, $refs[ConstantReferences::ASSEMBLY]);
}
if(isset($refs[ConstantReferences::BUILD]))
{
$value = ConstantCompiler::compileBuildConstants($value);
}
if(isset($refs[ConstantReferences::DATE_TIME]))
{
$value = ConstantCompiler::compileDateTimeConstants($value, $refs[ConstantReferences::DATE_TIME]);
}
if(isset($refs[ConstantReferences::INSTALL]))
{
$value = ConstantCompiler::compileInstallConstants($value, $refs[ConstantReferences::INSTALL]);
}
if(isset($refs[ConstantReferences::RUNTIME]))
{
$value = ConstantCompiler::compileRuntimeConstants($value);
}
return $value;
}
}

View file

@ -0,0 +1,388 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
/*
* 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;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IOException;
use ncc\Objects\Package\Component;
use ncc\Objects\Package\ExecutionUnit;
use ncc\Objects\Package\Metadata;
use ncc\Objects\ProjectConfiguration\Assembly;
use ncc\Objects\ProjectConfiguration\Installer;
use ncc\ZiProto\ZiProto;
use RuntimeException;
use ncc\Enums\PackageStructure;
class PackageReader
{
/**
* @var array
*/
private $headers;
/**
* @var int
*/
private $header_length;
/**
* @var resource
*/
private $package_file;
/**
* PackageReader constructor.
*
* @param string $file_path
* @throws IOException
*/
public function __construct(string $file_path)
{
if (!is_file($file_path))
{
throw new IOException(sprintf('File \'%s\' does not exist', $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));
}
$magic_bytes = fread($this->package_file, 7);
$header = '';
$diameter_hit = false;
$this->header_length = 7;
// Check for the magic bytes "ncc_pkg"
if($magic_bytes !== 'ncc_pkg')
{
throw new IOException(sprintf('File \'%s\' is not a valid package file (invalid magic bytes)', $file_path));
}
// Read everything after "ncc_pkg" up until the delimiter (0x1F 0x1F)
while(!feof($this->package_file))
{
$this->header_length++;
$header .= fread($this->package_file, 1);
if(str_ends_with($header, "\x1F\x1F"))
{
$diameter_hit = true;
$header = substr($header, 0, -2);
break;
}
// Stop at 100MB
if($this->header_length >= 100000000)
{
throw new IOException(sprintf('File \'%s\' is not a valid package file (header is too large)', $file_path));
}
}
if(!$diameter_hit)
{
throw new IOException(sprintf('File \'%s\' is not a valid package file (invalid header)', $file_path));
}
$this->headers = ZiProto::decode($header);
}
/**
* Returns the package headers
*
* @return array
*/
public function getHeaders(): array
{
return $this->headers;
}
/**
* Returns the package file version
*
* @return string
*/
public function getFileVersion(): string
{
return $this->headers[PackageStructure::FILE_VERSION];
}
/**
* Returns an array of flags from the package
*
* @return array
*/
public function getFlags(): array
{
return $this->headers[PackageStructure::FLAGS];
}
/**
* Returns a flag from the package
*
* @param string $name
* @return bool
*/
public function getFlag(string $name): bool
{
return in_array($name, $this->headers[PackageStructure::FLAGS], true);
}
/**
* Returns the directory of the package
*
* @return array
*/
public function getDirectory(): array
{
return $this->headers[PackageStructure::DIRECTORY];
}
/**
* Gets a resource from the package
*
* @param string $name
* @return string
*/
public function get(string $name): string
{
if(!isset($this->headers[PackageStructure::DIRECTORY][$name]))
{
throw new RuntimeException(sprintf('File \'%s\' not found in package', $name));
}
$location = explode(':', $this->headers[PackageStructure::DIRECTORY][$name]);
fseek($this->package_file, ($this->header_length + (int)$location[0]));
return fread($this->package_file, (int)$location[1]);
}
/**
* Returns the package's assembly
*
* @return Assembly
* @throws ConfigurationException
*/
public function getAssembly(): Assembly
{
if(!isset($this->headers[PackageStructure::DIRECTORY]['@assembly']))
{
throw new ConfigurationException('Package does not contain an assembly');
}
return Assembly::fromArray(ZiProto::decode($this->get('@assembly')));
}
/**
* Returns the package's metadata
*
* @return Metadata
* @throws ConfigurationException
*/
public function getMetadata(): Metadata
{
if(!isset($this->headers[PackageStructure::DIRECTORY]['@metadata']))
{
throw new ConfigurationException('Package does not contain metadata');
}
return Metadata::fromArray(ZiProto::decode($this->get('@metadata')));
}
/**
* Optional. Returns the package's installer
*
* @return Installer|null
*/
public function getInstaller(): ?Installer
{
if(!isset($this->headers[PackageStructure::DIRECTORY]['@installer']))
{
return null;
}
return Installer::fromArray(ZiProto::decode($this->get('@installer')));
}
/**
* Returns the package's dependencies
*
* @return array
*/
public function getDependencies(): array
{
$dependencies = [];
foreach($this->headers[PackageStructure::DIRECTORY] as $name => $location)
{
if(str_starts_with($name, '@dependencies:'))
{
$dependencies[] = str_replace('@dependencies:', '', $name);
}
}
return $dependencies;
}
/**
* Returns a dependency from the package
*
* @param string $name
* @return array
* @throws ConfigurationException
*/
public function getDependency(string $name): array
{
$dependency_name = sprintf('@dependencies:%s', $name);
if(!isset($this->headers[PackageStructure::DIRECTORY][$dependency_name]))
{
throw new ConfigurationException(sprintf('Dependency \'%s\' not found in package', $name));
}
return ZiProto::decode($this->get('@dependencies:' . $name));
}
/**
* Returns an array of execution units from the package
*
* @return array
*/
public function getExecutionUnits(): array
{
$execution_units = [];
foreach($this->headers[PackageStructure::DIRECTORY] as $name => $location)
{
if(str_starts_with($name, '@execution_units:'))
{
$execution_units[] = str_replace('@execution_units:', '', $name);
}
}
return $execution_units;
}
/**
* Returns an execution unit from the package
*
* @param string $name
* @return ExecutionUnit
* @throws ConfigurationException
*/
public function getExecutionUnit(string $name): ExecutionUnit
{
$execution_unit_name = sprintf('@execution_units:%s', $name);
if(!isset($this->headers[PackageStructure::DIRECTORY][$execution_unit_name]))
{
throw new ConfigurationException(sprintf('Execution unit \'%s\' not found in package', $name));
}
return ExecutionUnit::fromArray(ZiProto::decode($this->get($execution_unit_name)));
}
/**
* Returns the package's components
*
* @return array
*/
public function getComponents(): array
{
$components = [];
foreach($this->headers[PackageStructure::DIRECTORY] as $name => $location)
{
if(str_starts_with($name, '@components:'))
{
$components[] = str_replace('@components:', '', $name);
}
}
return $components;
}
/**
* Returns a component from the package
*
* @param string $name
* @return Component
* @throws ConfigurationException
*/
public function getComponent(string $name): Component
{
$component_name = sprintf('@components:%s', $name);
if(!isset($this->headers[PackageStructure::DIRECTORY][$component_name]))
{
throw new ConfigurationException(sprintf('Component \'%s\' not found in package', $name));
}
return Component::fromArray(ZiProto::decode($this->get('@components:' . $name)));
}
/**
* Returns an array of resources from the package
*
* @return array
*/
public function getResources(): array
{
$resources = [];
foreach($this->headers[PackageStructure::DIRECTORY] as $name => $location)
{
if(str_starts_with($name, '@resources:'))
{
$resources[] = str_replace('@resources:', '', $name);
}
}
return $resources;
}
/**
* Returns a resource from the package
*
* @param string $name
* @return string
* @throws ConfigurationException
*/
public function getResource(string $name): string
{
$resource_name = sprintf('@resources:%s', $name);
if(!isset($this->headers[PackageStructure::DIRECTORY][$resource_name]))
{
throw new ConfigurationException(sprintf('Resource \'%s\' not found in package', $name));
}
return $this->get('@resources:' . $name);
}
/**
* PackageReader destructor.
*/
public function __destruct()
{
if(is_resource($this->package_file))
{
fclose($this->package_file);
}
}
}

View file

@ -0,0 +1,331 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
/*
* 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;
use ncc\Enums\PackageStructure;
use ncc\Enums\PackageStructureVersions;
use ncc\Exceptions\IOException;
use ncc\Objects\Package\Component;
use ncc\Objects\Package\ExecutionUnit;
use ncc\Objects\Package\Metadata;
use ncc\Objects\Package\Resource;
use ncc\Objects\ProjectConfiguration\Assembly;
use ncc\Objects\ProjectConfiguration\Dependency;
use ncc\Objects\ProjectConfiguration\Installer;
use ncc\ZiProto\ZiProto;
class PackageWriter
{
/**
* @var array
*/
private $headers;
/**
* @var resource
*/
private $temp_file;
/**
* @var resource
*/
private $package_file;
/**
* @var string;
*/
private $temporary_path;
/**
* PackageWriter constructor.
*
* @param string $file_path
* @throws IOException
*/
public function __construct(string $file_path, bool $overwrite=true)
{
if(!$overwrite && is_file($file_path))
{
throw new IOException(sprintf('File \'%s\' already exists', $file_path));
}
if(is_file($file_path))
{
unlink($file_path);
}
if(is_file($file_path . '.tmp'))
{
unlink($file_path . '.tmp');
}
// Create the parent directory if it doesn't exist
if(!is_dir(dirname($file_path)))
{
if (!mkdir($concurrentDirectory = dirname($file_path), 0777, true) && !is_dir($concurrentDirectory))
{
throw new IOException(sprintf('Directory "%s" was not created', $concurrentDirectory));
}
}
touch($file_path);
touch($file_path . '.tmp');
$this->temporary_path = $file_path . '.tmp';
$this->temp_file = @fopen($this->temporary_path, 'wb'); // Create a temporary data file
$this->package_file = @fopen($file_path, 'wb');
$this->headers = [
PackageStructure::FILE_VERSION => PackageStructureVersions::_2_0,
PackageStructure::FLAGS => [],
PackageStructure::DIRECTORY => []
];
if($this->temp_file === false || $this->package_file === false)
{
throw new IOException(sprintf('Failed to open file \'%s\'', $file_path));
}
}
/**
* Returns the package file version
*
* @return string
*/
public function getFileVersion(): string
{
return (string)$this->headers[PackageStructure::FILE_VERSION];
}
/**
* Sets the package file version
*
* @param string $version
* @return void
*/
public function setFileVersion(string $version): void
{
$this->headers[PackageStructure::FILE_VERSION] = $version;
}
/**
* Returns the package flags
*
* @return array
*/
public function getFlags(): array
{
return (array)$this->headers[PackageStructure::FLAGS];
}
/**
* Sets the package flags
*
* @param array $flags
* @return void
*/
public function setFlags(array $flags): void
{
$this->headers[PackageStructure::FLAGS] = $flags;
}
/**
* Adds a flag to the package
*
* @param string $flag
* @return void
*/
public function addFlag(string $flag): void
{
if(!in_array($flag, $this->headers[PackageStructure::FLAGS], true))
{
$this->headers[PackageStructure::FLAGS][] = $flag;
}
}
/**
* Removes a flag from the package
*
* @param string $flag
* @return void
*/
public function removeFlag(string $flag): void
{
$this->headers[PackageStructure::FLAGS] = array_diff($this->headers[PackageStructure::FLAGS], [$flag]);
}
/**
* Adds a file to the package by writing to the temporary data file
*
* @param string $name
* @param string $data
* @return void
* @throws IOException
*/
public function add(string $name, string $data): void
{
if(isset($this->headers[PackageStructure::DIRECTORY][$name]))
{
throw new IOException(sprintf('Resource \'%s\' already exists in package', $name));
}
$this->headers[PackageStructure::DIRECTORY][$name] = sprintf("%d:%d", ftell($this->temp_file), strlen($data));
fwrite($this->temp_file, $data);
}
/**
* Sets the assembly of the package
*
* @param Assembly $assembly
* @return void
* @throws IOException
*/
public function setAssembly(Assembly $assembly): void
{
$this->add('@assembly', ZiProto::encode($assembly->toArray()));
}
/**
* Adds the metadata to the package
*
* @param Metadata $metadata
* @return void
* @throws IOException
*/
public function setMetadata(Metadata $metadata): void
{
$this->add('@metadata', ZiProto::encode($metadata->toArray()));
}
/**
* Sets the installer information of the package
*
* @param Installer $installer
* @return void
* @throws IOException
*/
public function setInstaller(Installer $installer): void
{
$this->add('@installer', ZiProto::encode($installer->toArray()));
}
/**
* Adds a dependency configuration to the package
*
* @param Dependency $dependency
* @return void
* @throws IOException
*/
public function addDependencyConfiguration(Dependency $dependency): void
{
$this->add(sprintf('@dependencies:%s', $dependency->getName()), ZiProto::encode($dependency->toArray()));
}
/**
* Adds an execution unit to the package
*
* @param ExecutionUnit $unit
* @return void
* @throws IOException
*/
public function addExecutionUnit(ExecutionUnit $unit): void
{
$this->add(sprintf('@execution_units:%s', $unit->getExecutionPolicy()->getName()), ZiProto::encode($unit->toArray()));
}
/**
* Adds a component to the package
*
* @param Component $component
* @return void
* @throws IOException
*/
public function addComponent(Component $component): void
{
$this->add(sprintf('@components:%s', $component->getName()), ZiProto::encode($component->toArray()));
}
/**
* Adds a resource to the package
*
* @param Resource $resource
* @return void
* @throws IOException
*/
public function addResource(Resource $resource): void
{
$this->add(sprintf('@resources:%s', $resource->getName()), $resource->getData());
}
/**
* Finalizes the package by writing the magic bytes, header length, delimiter, headers, and data to the file
*
* @return void
* @throws IOException
*/
public function close(): void
{
if(!is_resource($this->package_file) || !is_resource($this->temp_file))
{
throw new IOException('Package is already closed');
}
// Close the temporary data file
fclose($this->temp_file);
// Write the magic bytes "ncc_pkg" to the package and the header
fwrite($this->package_file, 'ncc_pkg');
fwrite($this->package_file, ZiProto::encode($this->headers));
fwrite($this->package_file, chr(0x1F) . chr(0x1F));
// Copy the temporary data file to the package
$temp_file = fopen($this->temporary_path, 'rb');
stream_copy_to_stream($temp_file, $this->package_file);
// Close the file handles
fclose($this->package_file);
fclose($temp_file);
unlink($this->temporary_path);
$this->package_file = null;
$this->temp_file = null;
}
/**
* Closes the package when the object is destroyed
*/
public function __destruct()
{
try
{
$this->close();
}
catch(IOException $e)
{
// Ignore
}
}
}

View file

@ -0,0 +1,65 @@
<?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;
use Exception;
use ncc\Enums\ComponentDataType;
use ncc\Exceptions\IOException;
use ncc\Exceptions\PathNotFoundException;
use ncc\Objects\Package\Component;
use ncc\ThirdParty\nikic\PhpParser\ParserFactory;
use ncc\Utilities\Base64;
use ncc\Utilities\Console;
use ncc\Utilities\Functions;
use ncc\Utilities\IO;
use ncc\ZiProto\ZiProto;
class NccCompiler extends \ncc\Classes\NccExtension\NccCompiler
{
/**
* @param string $file_path
* @return Component
* @throws IOException
* @throws PathNotFoundException
*/
public function buildComponent(string $file_path): Component
{
$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
try
{
$encoded = json_encode($parser->parse(IO::fread($file_path)), JSON_THROW_ON_ERROR);
return new Component(Functions::removeBasename($file_path), ZiProto::encode(json_decode($encoded, true, 512, JSON_THROW_ON_ERROR)), ComponentDataType::AST);
}
catch(Exception $e)
{
Console::outWarning(sprintf('Failed to compile file "%s" with error "%s"', $file_path, $e->getMessage()));
}
return new Component(
Functions::removeBasename($file_path),
Base64::encode(IO::fread($file_path)), ComponentDataType::BASE64_ENCODED
);
}
}

View file

@ -1,488 +0,0 @@
<?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.
*
*/
/** @noinspection PhpMissingFieldTypeInspection */
namespace ncc\Classes\PhpExtension;
use Exception;
use FilesystemIterator;
use ncc\Enums\ComponentFileExtensions;
use ncc\Enums\ComponentDataType;
use ncc\Enums\ConstantReferences;
use ncc\Enums\DependencySourceType;
use ncc\Enums\Options\BuildConfigurationValues;
use ncc\Classes\NccExtension\PackageCompiler;
use ncc\Exceptions\BuildException;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\NotSupportedException;
use ncc\Exceptions\OperationException;
use ncc\Exceptions\PackageException;
use ncc\Exceptions\PathNotFoundException;
use ncc\Interfaces\CompilerInterface;
use ncc\Managers\PackageLockManager;
use ncc\Objects\Package;
use ncc\Objects\ProjectConfiguration;
use ncc\Objects\ProjectConfiguration\Dependency;
use ncc\ThirdParty\nikic\PhpParser\ParserFactory;
use ncc\ThirdParty\Symfony\Filesystem\Filesystem;
use ncc\ThirdParty\theseer\DirectoryScanner\DirectoryScanner;
use ncc\Utilities\Base64;
use ncc\Utilities\Console;
use ncc\Utilities\Functions;
use ncc\Utilities\IO;
use SplFileInfo;
class PhpCompiler implements CompilerInterface
{
/**
* @var ProjectConfiguration
*/
private $project_configuration;
/**
* @var Package|null
*/
private $package;
/**
* @var string
*/
private $path;
/**
* @param ProjectConfiguration $project
* @param string $path
*/
public function __construct(ProjectConfiguration $project, string $path)
{
$this->project_configuration = $project;
$this->path = $path;
}
/**
* Prepares the PHP package by generating the Autoloader and detecting all components & resources
* This function must be called before calling the build function, otherwise the operation will fail
*
* @param string $build_configuration
* @return void
* @throws ConfigurationException
* @throws OperationException
* @throws PackageException
*/
public function prepare(string $build_configuration=BuildConfigurationValues::DEFAULT): void
{
try
{
/** @noinspection PhpRedundantOptionalArgumentInspection */
$this->project_configuration->validate(True);
}
catch (Exception $e)
{
throw new PackageException($e->getMessage(), $e);
}
// Select the build configuration
$selected_build_configuration = $this->project_configuration->getBuild()->getBuildConfiguration($build_configuration);
// Create the package object
$this->package = new Package();
$this->package->setAssembly($this->project_configuration->getAssembly());
$this->package->setDependencies($this->project_configuration->getBuild()->getDependencies());
$this->package->setMainExecutionPolicy($this->project_configuration->getBuild()->getMain());
// Add the option to create a symbolic link to the package
if(isset($this->project_configuration->getProject()->getOptions()['create_symlink']) && $this->project_configuration->getProject()->getOptions()['create_symlink'] === True)
{
$this->package->getHeader()->setOption('create_symlink', true);
}
// Add both the defined constants from the build configuration and the global constants.
// Global constants are overridden
$this->package->getHeader()->setRuntimeConstants(array_merge(
$selected_build_configuration->getDefineConstants(),
($this->project_configuration->getBuild()->getDefineConstants()),
($this->package->getHeader()->getRuntimeConstants() ?? [])
));
$this->package->getHeader()->setCompilerExtension($this->project_configuration->getProject()->getCompiler());
$this->package->getHeader()->setCompilerVersion(NCC_VERSION_NUMBER);
$this->package->getHeader()->setOptions($this->project_configuration->getProject()->getOptions());
if($this->project_configuration->getProject()->getUpdateSource() !== null)
{
$this->package->getHeader()->setUpdateSource($this->project_configuration->getProject()->getUpdateSource());
}
Console::outDebug('scanning project files');
Console::outDebug('theseer\DirectoryScanner - Copyright (c) 2009-2014 Arne Blankerts <arne@blankerts.de> All rights reserved.');
// First scan the project files and create a file struct.
$directory_scanner = new DirectoryScanner();
try
{
$directory_scanner->unsetFlag(FilesystemIterator::FOLLOW_SYMLINKS);
}
catch (Exception $e)
{
throw new PackageException('Cannot unset flag \'FOLLOW_SYMLINKS\' in DirectoryScanner, ' . $e->getMessage(), $e);
}
// Include file components that can be compiled
$directory_scanner->setIncludes(ComponentFileExtensions::PHP);
if(count($selected_build_configuration->getExcludeFiles()) > 0)
{
$directory_scanner->setExcludes($selected_build_configuration->getExcludeFiles());
}
$source_path = $this->path . $this->project_configuration->getBuild()->getSourcePath();
// TODO: Re-implement the scanning process outside the compiler, as this is will be redundant
// Scan for components first.
if(file_exists($source_path))
{
Console::outVerbose('Scanning for components... ');
/** @var SplFileInfo $item */
/** @noinspection PhpRedundantOptionalArgumentInspection */
foreach($directory_scanner($source_path, True) as $item)
{
// Ignore directories, they're not important. :-)
if(is_dir($item->getPathName()))
{
continue;
}
$component = new Package\Component();
$component->setName(Functions::removeBasename($item->getPathname(), $this->path));
$this->package->addComponent($component);
Console::outVerbose(sprintf('Found component %s', $component->getName()));
}
if(count($this->package->getComponents()) > 0)
{
Console::outVerbose(count($this->package->getComponents()) . ' component(s) found');
}
else
{
Console::outVerbose('No components found');
}
// Clear previously excludes and includes
$directory_scanner->setExcludes();
$directory_scanner->setIncludes();
// Ignore component files
if(count($selected_build_configuration->getExcludeFiles()) > 0)
{
$directory_scanner->setExcludes(array_merge($selected_build_configuration->getExcludeFiles(), ComponentFileExtensions::PHP));
}
else
{
$directory_scanner->setExcludes(ComponentFileExtensions::PHP);
}
Console::outVerbose('Scanning for resources... ');
/** @var SplFileInfo $item */
foreach($directory_scanner($source_path) as $item)
{
// Ignore directories, they're not important. :-)
if(is_dir($item->getPathName()))
{
continue;
}
$resource = new Package\Resource();
$resource->setName(Functions::removeBasename($item->getPathname(), $this->path));
$this->package->addResource($resource);
Console::outVerbose(sprintf('found resource %s', $resource->getName()));
}
if(count($this->package->getResources()) > 0)
{
Console::outVerbose(count($this->package->getResources()) . ' resources(s) found');
}
else
{
Console::outVerbose('No resources found');
}
}
else
{
Console::outWarning('Source path does not exist, skipping resource and component scanning');
}
$selected_dependencies = [];
if(count($this->project_configuration->getBuild()->getDependencies()) > 0)
{
$selected_dependencies = array_merge($selected_dependencies, $this->project_configuration->getBuild()->getDependencies());
}
if(count($selected_build_configuration->getDependencies()) > 0)
{
$selected_dependencies = array_merge($selected_dependencies, $selected_build_configuration->getDependencies());
}
// Process the dependencies
if(count($selected_dependencies) > 0)
{
$package_lock_manager = new PackageLockManager();
$filesystem = new Filesystem();
$lib_path = $selected_build_configuration->getOutputPath() . DIRECTORY_SEPARATOR . 'libs';
if($filesystem->exists($lib_path))
{
$filesystem->remove($lib_path);
}
Console::outVerbose('Scanning for dependencies... ');
/** @var Dependency $dependency */
foreach($selected_dependencies as $dependency)
{
Console::outVerbose(sprintf('processing dependency %s', $dependency->getName()));
switch($dependency->getSourceType())
{
case DependencySourceType::STATIC:
try
{
$out_path = $lib_path . DIRECTORY_SEPARATOR . sprintf('%s=%s.lib', $dependency->getName(), $dependency->getVersion());
$package = $package_lock_manager->getPackageLock()?->getPackage($dependency->getName());
if($package === null)
{
throw new IOException('Cannot find package lock for dependency ' . $dependency->getName());
}
$version = $package->getVersion($dependency->getVersion());
if($version === null)
{
throw new OperationException('Cannot find version ' . $dependency->getVersion() . ' for dependency ' . $dependency->getName());
}
Console::outDebug(sprintf('copying shadow package %s=%s to %s', $dependency->getName(), $dependency->getVersion(), $out_path));
if(!$filesystem->exists($lib_path))
{
$filesystem->mkdir($lib_path);
}
$filesystem->copy($version->location, $out_path);
$dependency->Source = 'libs' . DIRECTORY_SEPARATOR . sprintf('%s=%s.lib', $dependency->getName(), $dependency->getVersion());
}
catch (IOException $e)
{
throw new PackageException('Static linking not possible, cannot find package lock for dependency ' . $dependency->getName(), $e);
}
break;
default:
case DependencySourceType::REMOTE:
break;
}
$this->package->addDependency($dependency);
}
if(count($this->package->getDependencies()) > 0)
{
Console::outVerbose(count($this->package->getDependencies()) . ' dependency(ies) found');
}
else
{
Console::outVerbose('No dependencies found');
}
}
}
/**
* Executes the compile process in the correct order and returns the finalized Package object
*
* @return Package|null
* @throws BuildException
* @throws IOException
* @throws NotSupportedException
* @throws PathNotFoundException
*/
public function build(): ?Package
{
$this->compileExecutionPolicies();
$this->compileComponents();
$this->compileResources();
PackageCompiler::compilePackageConstants($this->package, [
ConstantReferences::ASSEMBLY => $this->project_configuration->getAssembly(),
ConstantReferences::BUILD => null,
ConstantReferences::DATE_TIME => time()
]);
return $this->getPackage();
}
/**
* Compiles the resources of the package
*
* @return void
* @throws BuildException
* @throws IOException
* @throws PathNotFoundException
*/
public function compileResources(): void
{
if($this->package === null)
{
throw new BuildException('The prepare() method must be called before building the package');
}
if(count($this->package->getResources()) === 0)
{
return;
}
// Process the resources
$total_items = count($this->package->getResources());
$processed_items = 1;
$resources = [];
if($total_items > 5)
{
Console::out('Processing resources');
}
foreach($this->package->getResources() as $resource)
{
/** @noinspection DisconnectedForeachInstructionInspection */
if($total_items > 5)
{
Console::inlineProgressBar($processed_items, $total_items);
}
// Get the data and
$resource->setData(Base64::encode(IO::fread(Functions::correctDirectorySeparator($this->path . $resource->getName()))));
$resource->setName(str_replace($this->project_configuration->getBuild()->getSourcePath(), (string)null, $resource->getName()));
$resource->updateChecksum();
$resources[] = $resource;
Console::outDebug(sprintf('processed resource %s', $resource->getName()));
}
// Update the resources
$this->package->setResources($resources);
}
/**
* Compiles the components of the package
*
* @return void
* @throws BuildException
* @throws IOException
* @throws PathNotFoundException
*/
public function compileComponents(): void
{
if($this->package === null)
{
throw new BuildException('The prepare() method must be called before building the package');
}
if(count($this->package->getComponents()) === 0)
{
return;
}
$total_items = count($this->package->getComponents());
$processed_items = 1;
$components = [];
if($total_items > 5)
{
Console::out('Compiling components');
}
// Process the components and attempt to create an AST representation of the source
foreach($this->package->getComponents() as $component)
{
if($total_items > 5)
{
Console::inlineProgressBar($processed_items, $total_items);
}
$content = IO::fread(Functions::correctDirectorySeparator($this->path . $component->getName()));
$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
try
{
$stmts = $parser->parse($content);
$encoded = json_encode($stmts, JSON_THROW_ON_ERROR);
unset($stmts);
$component->setDataType(ComponentDataType::AST);
$component->setData(json_decode($encoded, true, 512, JSON_THROW_ON_ERROR));
}
catch(Exception $e)
{
$component->setDataType(ComponentDataType::BASE64_ENCODED);
$component->setData(Base64::encode($content));
unset($e);
}
unset($parser);
$component->setName(str_replace($this->project_configuration->getBuild()->getSourcePath(), (string)null, $component->getName()));
$component->updateChecksum();
$components[] = $component;
++$processed_items;
Console::outDebug(sprintf('processed component %s (%s)', $component->getName(), $component->getDataType()));
}
// Update the components
$this->package->setComponents($components);
}
/**
* @return void
* @throws IOException
* @throws NotSupportedException
* @throws PathNotFoundException
*/
public function compileExecutionPolicies(): void
{
$this->package->setExecutionUnits(PackageCompiler::compileExecutionPolicies($this->path, $this->project_configuration));
}
/**
* @inheritDoc
*/
public function getPackage(): ?Package
{
return $this->package;
}
}

View file

@ -84,7 +84,7 @@
return null; return null;
} }
if(!$component->validate_checksum()) if(!$component->validateChecksum())
{ {
throw new IntegrityException(sprintf('Checksum validation failed for component: %s', $component->getName())); throw new IntegrityException(sprintf('Checksum validation failed for component: %s', $component->getName()));
} }

View file

@ -0,0 +1,28 @@
<?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\Enums;
final class BuildOutputType
{
public const NCC_PACKAGE = 'ncc';
}

View file

@ -0,0 +1,32 @@
<?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\Enums;
final class PackageStructure
{
public const FILE_VERSION = 0x73746669;
public const FLAGS = 0x73736166;
public const DIRECTORY = 0x6f746365;
}

View file

@ -24,9 +24,21 @@ namespace ncc\Enums;
final class PackageStructureVersions final class PackageStructureVersions
{ {
/**
* ncc 1.0.0 to 1.0.3
*/
public const _1_0 = '1.0'; public const _1_0 = '1.0';
/**
* ncc 1.0.4 and above
*/
public const _2_0 = '2.0';
/**
* All supported versions
*/
public const ALL = [ public const ALL = [
self::_1_0 self::_1_0,
self::_2_0
]; ];
} }

View file

@ -124,7 +124,7 @@
return $package->getAssembly()->getPackage(); return $package->getAssembly()->getPackage();
} }
$extension = $package->getHeader()->getCompilerExtension()->getExtension(); $extension = $package->getMetadata()->getCompilerExtension()->getExtension();
$installation_paths = new InstallationPaths($this->packages_path . DIRECTORY_SEPARATOR . $package->getAssembly()->getPackage() . '=' . $package->getAssembly()->getVersion()); $installation_paths = new InstallationPaths($this->packages_path . DIRECTORY_SEPARATOR . $package->getAssembly()->getPackage() . '=' . $package->getAssembly()->getVersion());
$installer = match ($extension) $installer = match ($extension)
@ -191,7 +191,7 @@
Console::outDebug(sprintf('assembly.%s: %s', $prop, ($value ?? 'n/a'))); Console::outDebug(sprintf('assembly.%s: %s', $prop, ($value ?? 'n/a')));
} }
foreach($package->getHeader()->getCompilerExtension()->toArray() as $prop => $value) foreach($package->getMetadata()->getCompilerExtension()->toArray() as $prop => $value)
{ {
Console::outDebug(sprintf('header.compiler.%s: %s', $prop, ($value ?? 'n/a'))); Console::outDebug(sprintf('header.compiler.%s: %s', $prop, ($value ?? 'n/a')));
} }
@ -361,7 +361,7 @@
} }
// After execution units are installed, create a symlink if needed // After execution units are installed, create a symlink if needed
if(!is_null($package->getHeader()->getOption('create_symlink')) && $package->getHeader()->getOption('create_symlink')) if(!is_null($package->getMetadata()->getOption('create_symlink')) && $package->getMetadata()->getOption('create_symlink'))
{ {
if($package->getMainExecutionPolicy() === null) if($package->getMainExecutionPolicy() === null)
{ {
@ -415,18 +415,18 @@
Console::outDebug('no post-installation units to execute'); Console::outDebug('no post-installation units to execute');
} }
if($package->getHeader()->getUpdateSource()?->getRepository() !== null) if($package->getMetadata()->getUpdateSource()?->getRepository() !== null)
{ {
$sources_manager = new RemoteSourcesManager(); $sources_manager = new RemoteSourcesManager();
if($sources_manager->getRemoteSource($package->getHeader()->getUpdateSource()->getRepository()->getName()) === null) if($sources_manager->getRemoteSource($package->getMetadata()->getUpdateSource()->getRepository()->getName()) === null)
{ {
Console::outVerbose('Adding remote source ' . $package->getHeader()->getUpdateSource()->getRepository()->getName()); Console::outVerbose('Adding remote source ' . $package->getMetadata()->getUpdateSource()->getRepository()->getName());
$defined_remote_source = new DefinedRemoteSource(); $defined_remote_source = new DefinedRemoteSource();
$defined_remote_source->setName($package->getHeader()->getUpdateSource()?->getRepository()?->getName()); $defined_remote_source->setName($package->getMetadata()->getUpdateSource()?->getRepository()?->getName());
$defined_remote_source->setHost($package->getHeader()->getUpdateSource()?->getRepository()?->getHost()); $defined_remote_source->setHost($package->getMetadata()->getUpdateSource()?->getRepository()?->getHost());
$defined_remote_source->setType($package->getHeader()->getUpdateSource()?->getRepository()?->getType()); $defined_remote_source->setType($package->getMetadata()->getUpdateSource()?->getRepository()?->getType());
$defined_remote_source->setSsl($package->getHeader()->getUpdateSource()?->getRepository()?->isSsl()); $defined_remote_source->setSsl($package->getMetadata()->getUpdateSource()?->getRepository()?->isSsl());
$sources_manager->addRemoteSource($defined_remote_source); $sources_manager->addRemoteSource($defined_remote_source);
} }
@ -995,8 +995,8 @@
$data_files = [ $data_files = [
$paths->getDataPath() . DIRECTORY_SEPARATOR . 'assembly' => ZiProto::encode($package->getAssembly()->toArray(true)), $paths->getDataPath() . DIRECTORY_SEPARATOR . 'assembly' => ZiProto::encode($package->getAssembly()->toArray(true)),
$paths->getDataPath() . DIRECTORY_SEPARATOR . 'ext' => ZiProto::encode($package->getHeader()->getCompilerExtension()->toArray()), $paths->getDataPath() . DIRECTORY_SEPARATOR . 'ext' => ZiProto::encode($package->getMetadata()->getCompilerExtension()->toArray()),
$paths->getDataPath() . DIRECTORY_SEPARATOR . 'const' => ZiProto::encode($package->getHeader()->getRuntimeConstants()), $paths->getDataPath() . DIRECTORY_SEPARATOR . 'const' => ZiProto::encode($package->getMetadata()->getRuntimeConstants()),
$paths->getDataPath() . DIRECTORY_SEPARATOR . 'dependencies' => ZiProto::encode($dependencies), $paths->getDataPath() . DIRECTORY_SEPARATOR . 'dependencies' => ZiProto::encode($dependencies),
]; ];

View file

@ -25,23 +25,26 @@
namespace ncc\Managers; namespace ncc\Managers;
use JetBrains\PhpStorm\NoReturn; use ncc\Classes\PhpExtension\NccCompiler;
use ncc\Classes\PhpExtension\PhpCliTemplate; use ncc\Classes\PhpExtension\PhpCliTemplate;
use ncc\Classes\PhpExtension\PhpLibraryTemplate; use ncc\Classes\PhpExtension\PhpLibraryTemplate;
use ncc\Enums\BuildOutputType;
use ncc\Enums\CompilerExtensions;
use ncc\Enums\ComponentFileExtensions;
use ncc\Enums\Options\BuildConfigurationValues; use ncc\Enums\Options\BuildConfigurationValues;
use ncc\Enums\Options\InitializeProjectOptions; use ncc\Enums\Options\InitializeProjectOptions;
use ncc\Classes\NccExtension\PackageCompiler;
use ncc\Enums\ProjectTemplates; use ncc\Enums\ProjectTemplates;
use ncc\Exceptions\BuildException; use ncc\Exceptions\BuildException;
use ncc\Exceptions\ConfigurationException; use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IOException; use ncc\Exceptions\IOException;
use ncc\Exceptions\NotSupportedException; use ncc\Exceptions\NotSupportedException;
use ncc\Exceptions\PathNotFoundException; use ncc\Exceptions\PathNotFoundException;
use ncc\Objects\Package\ExecutionUnit;
use ncc\Objects\ProjectConfiguration; use ncc\Objects\ProjectConfiguration;
use ncc\Objects\ProjectConfiguration\Compiler; use ncc\Objects\ProjectConfiguration\Compiler;
use ncc\Utilities\Console; use ncc\Utilities\Console;
use ncc\Utilities\Functions; use ncc\Utilities\Functions;
use ncc\Utilities\Validate; use ncc\Utilities\IO;
class ProjectManager class ProjectManager
{ {
@ -148,7 +151,16 @@
*/ */
public function build(string $build_configuration=BuildConfigurationValues::DEFAULT): string public function build(string $build_configuration=BuildConfigurationValues::DEFAULT): string
{ {
return PackageCompiler::compile($this, $build_configuration); $configuration = $this->project_configuration->getBuild()->getBuildConfiguration($build_configuration);
return match (strtolower($this->project_configuration->getProject()->getCompiler()->getExtension()))
{
CompilerExtensions::PHP => match (strtolower($configuration->getBuildType())) {
BuildOutputType::NCC_PACKAGE => (new NccCompiler($this))->build($build_configuration),
default => throw new BuildException(sprintf('php cannot produce the build type \'%s\'', $configuration->getBuildType())),
},
default => throw new NotSupportedException(sprintf('The compiler extension \'%s\' is not supported', $this->project_configuration->getProject()->getCompiler()->getExtension())),
};
} }
/** /**
@ -178,6 +190,124 @@
} }
} }
/**
* Returns an array of file extensions for the components that are part of this project
*
* @return array
* @throws NotSupportedException
*/
public function getComponentFileExtensions(): array
{
return match ($this->getProjectConfiguration()->getProject()->getCompiler()->getExtension())
{
CompilerExtensions::PHP => ComponentFileExtensions::PHP,
default => throw new NotSupportedException(
sprintf('The compiler extension \'%s\' is not supported', $this->getProjectConfiguration()->getProject()->getCompiler()->getExtension())
),
};
}
/**
* Returns an array of ExecutionUnits associated with the project by selecting all required execution units
* from the project configuration and reading the contents of the files
*
* @param string $build_configuration
* @return ExecutionUnit[]
* @throws ConfigurationException
* @throws IOException
* @throws PathNotFoundException
*/
public function getExecutionUnits(string $build_configuration=BuildConfigurationValues::DEFAULT): array
{
$execution_units = [];
foreach($this->project_configuration->getRequiredExecutionPolicies($build_configuration) as $policy)
{
$execution_policy = $this->project_configuration->getExecutionPolicy($policy);
$execution_file = $this->getProjectPath() . DIRECTORY_SEPARATOR . $execution_policy->getExecute()->getTarget();
if(!is_file($execution_file))
{
throw new IOException(sprintf('The execution policy %s points to a non-existent file \'%s\'', $execution_policy->getName(), $execution_file));
}
$execution_units[] = new ExecutionUnit($execution_policy, IO::fread($execution_file));
}
return $execution_units;
}
/**
* Returns an array of file paths for the components that are part of this project
*
* @param string $build_configuration
* @return array
* @throws ConfigurationException
* @throws NotSupportedException
*/
public function getComponents(string $build_configuration=BuildConfigurationValues::DEFAULT): array
{
$configuration = $this->project_configuration->getBuild()->getBuildConfiguration($build_configuration);
return array_map(static function ($file) {
return $file;
}, Functions::scanDirectory($this->getProjectSourcePath(), $this->getComponentFileExtensions(), $configuration->getExcludeFiles()));
}
/**
* Returns an array of file paths for the resources that are part of this project
*
* @param string $build_configuration
* @return array
* @throws ConfigurationException
* @throws NotSupportedException
*/
public function getResources(string $build_configuration=BuildConfigurationValues::DEFAULT): array
{
$configuration = $this->project_configuration->getBuild()->getBuildConfiguration($build_configuration);
return array_map(static function ($file) {
return $file;
}, Functions::scanDirectory($this->getProjectSourcePath(), [], array_merge(
$configuration->getExcludeFiles(), $this->getComponentFileExtensions()
)));
}
/**
* Returns an array of runtime constants for the project & build configuration
*
* @param string $build_configuration
* @return array
* @throws ConfigurationException
*/
public function getRuntimeConstants(string $build_configuration=BuildConfigurationValues::DEFAULT): array
{
$configuration = $this->project_configuration->getBuild()->getBuildConfiguration($build_configuration);
/** @noinspection ArrayMergeMissUseInspection */
return array_merge(
$configuration->getDefineConstants(),
$this->project_configuration->getBuild()->getDefineConstants()
);
}
/**
* Returns an array of compiler options associated with the build configuration
*
* @param string $build_configuration
* @return array
* @throws ConfigurationException
*/
public function getCompilerOptions(string $build_configuration=BuildConfigurationValues::DEFAULT): array
{
$configuration = $this->project_configuration->getBuild()->getBuildConfiguration($build_configuration);
/** @noinspection ArrayMergeMissUseInspection */
return array_merge(
$configuration->getOptions(),
$this->project_configuration->getBuild()->getOptions()
);
}
/** /**
* Initializes the project structure * Initializes the project structure
* *

View file

@ -34,11 +34,12 @@
use ncc\Interfaces\BytecodeObjectInterface; use ncc\Interfaces\BytecodeObjectInterface;
use ncc\Objects\Package\Component; use ncc\Objects\Package\Component;
use ncc\Objects\Package\ExecutionUnit; use ncc\Objects\Package\ExecutionUnit;
use ncc\Objects\Package\Header; use ncc\Objects\Package\Metadata;
use ncc\Objects\Package\Installer; use ncc\Objects\Package\Installer;
use ncc\Objects\Package\MagicBytes; use ncc\Objects\Package\MagicBytes;
use ncc\Objects\Package\Resource; use ncc\Objects\Package\Resource;
use ncc\Objects\ProjectConfiguration\Assembly; use ncc\Objects\ProjectConfiguration\Assembly;
use ncc\Objects\ProjectConfiguration\Compiler;
use ncc\Objects\ProjectConfiguration\Dependency; use ncc\Objects\ProjectConfiguration\Dependency;
use ncc\Utilities\Functions; use ncc\Utilities\Functions;
use ncc\Utilities\IO; use ncc\Utilities\IO;
@ -56,9 +57,9 @@
/** /**
* The true header of the package * The true header of the package
* *
* @var Header * @var Metadata
*/ */
private $header; private $metadata;
/** /**
* The assembly object of the package * The assembly object of the package
@ -74,13 +75,6 @@
*/ */
private $dependencies; private $dependencies;
/**
* The Main Execution Policy object for the package if the package is an executable package.
*
* @var string|null
*/
private $main_execution_policy;
/** /**
* The installer object that is used to install the package if the package is install-able * The installer object that is used to install the package if the package is install-able
* *
@ -112,11 +106,11 @@
/** /**
* Public Constructor * Public Constructor
*/ */
public function __construct() public function __construct(Assembly $assembly, Compiler $compiler)
{ {
$this->magic_bytes = new MagicBytes(); $this->magic_bytes = new MagicBytes();
$this->header = new Header(); $this->metadata = new Metadata($compiler);
$this->assembly = new Assembly(); $this->assembly = $assembly;
$this->execution_units = []; $this->execution_units = [];
$this->components = []; $this->components = [];
$this->dependencies = []; $this->dependencies = [];
@ -178,19 +172,19 @@
} }
/** /**
* @return Header * @return Metadata
*/ */
public function getHeader(): Header public function getMetadata(): Metadata
{ {
return $this->header; return $this->metadata;
} }
/** /**
* @param Header $header * @param Metadata $metadata
*/ */
public function setHeader(Header $header): void public function setMetadata(Metadata $metadata): void
{ {
$this->header = $header; $this->metadata = $metadata;
} }
/** /**
@ -273,6 +267,24 @@
$this->execution_units = $execution_units; $this->execution_units = $execution_units;
} }
/**
* @param ExecutionUnit $unit
* @return void
*/
public function addExecutionUnit(ExecutionUnit $unit): void
{
foreach($this->execution_units as $exec_unit)
{
if($exec_unit->getId() === $unit->getId())
{
$this->removeExecutionUnit($exec_unit->getId());
break;
}
}
$this->execution_units[] = $unit;
}
/** /**
* @return array|Resource[] * @return array|Resource[]
*/ */
@ -590,7 +602,7 @@
} }
return [ return [
($bytecode ? Functions::cbc('header') : 'header') => $this?->header?->toArray($bytecode), ($bytecode ? Functions::cbc('header') : 'header') => $this?->metadata?->toArray($bytecode),
($bytecode ? Functions::cbc('assembly') : 'assembly') => $this?->assembly?->toArray($bytecode), ($bytecode ? Functions::cbc('assembly') : 'assembly') => $this?->assembly?->toArray($bytecode),
($bytecode ? Functions::cbc('dependencies') : 'dependencies') => $_dependencies, ($bytecode ? Functions::cbc('dependencies') : 'dependencies') => $_dependencies,
($bytecode ? Functions::cbc('main_execution_policy') : 'main_execution_policy') => $this?->main_execution_policy, ($bytecode ? Functions::cbc('main_execution_policy') : 'main_execution_policy') => $this?->main_execution_policy,
@ -608,10 +620,10 @@
{ {
$object = new self(); $object = new self();
$object->header = Functions::array_bc($data, 'header'); $object->metadata = Functions::array_bc($data, 'header');
if($object->header !== null) if($object->metadata !== null)
{ {
$object->header = Header::fromArray($object->header); $object->metadata = Metadata::fromArray($object->metadata);
} }
$object->assembly = Functions::array_bc($data, 'assembly'); $object->assembly = Functions::array_bc($data, 'assembly');

View file

@ -24,6 +24,8 @@
namespace ncc\Objects\Package; namespace ncc\Objects\Package;
use ncc\Enums\ComponentDataType;
use ncc\Exceptions\ConfigurationException;
use ncc\Interfaces\BytecodeObjectInterface; use ncc\Interfaces\BytecodeObjectInterface;
use ncc\Utilities\Functions; use ncc\Utilities\Functions;
@ -61,46 +63,35 @@
/** /**
* The raw data of the component, this is to be processed by the compiler extension * The raw data of the component, this is to be processed by the compiler extension
* *
* @var mixed * @var string
*/ */
private $data; private $data;
/**
* @param string $name
* @param string $data
* @param string $data_type
*/
public function __construct(string $name, string $data, string $data_type=ComponentDataType::PLAIN)
{
$this->name = $name;
$this->flags = [];
$this->data_type = $data_type;
$this->data = $data;
$this->checksum = hash('sha1', $data, true);
}
/** /**
* Validates the checksum of the component, returns false if the checksum or data is invalid or if the checksum * Validates the checksum of the component, returns false if the checksum or data is invalid or if the checksum
* failed. * failed.
* *
* @return bool * @return bool
*/ */
public function validate_checksum(): bool public function validateChecksum(): bool
{ {
if($this->checksum === null)
{
return true; // Return true if the checksum is empty
}
if($this->data === null)
{
return true; // Return true if the data is null
}
return hash_equals($this->checksum, hash('sha1', $this->data, true)); return hash_equals($this->checksum, hash('sha1', $this->data, true));
} }
/**
* Updates the checksum of the resource
*
* @return void
*/
public function updateChecksum(): void
{
$this->checksum = null;
if(is_string($this->data))
{
$this->checksum = hash('sha1', $this->data, true);
}
}
/** /**
* @return string * @return string
*/ */
@ -148,7 +139,8 @@
*/ */
public function removeFlag(string $flag): void public function removeFlag(string $flag): void
{ {
$this->flags = array_filter($this->flags, static function($f) use ($flag) { $this->flags = array_filter($this->flags, static function($f) use ($flag)
{
return $f !== $flag; return $f !== $flag;
}); });
} }
@ -161,14 +153,6 @@
return $this->data_type; return $this->data_type;
} }
/**
* @param string $data_type
*/
public function setDataType(string $data_type): void
{
$this->data_type = $data_type;
}
/** /**
* @return string * @return string
*/ */
@ -178,27 +162,22 @@
} }
/** /**
* @param string $checksum * @return string
*/ */
public function setChecksum(string $checksum): void public function getData(): string
{
$this->checksum = $checksum;
}
/**
* @return mixed
*/
public function getData(): mixed
{ {
return $this->data; return $this->data;
} }
/** /**
* @param mixed $data * @param mixed $data
* @param string $data_type
*/ */
public function setData(mixed $data): void public function setData(mixed $data, string $data_type=ComponentDataType::PLAIN): void
{ {
$this->data = $data; $this->data = $data;
$this->data_type = $data_type;
$this->checksum = hash('sha1', $data, true);
} }
/** /**
@ -223,16 +202,28 @@
* *
* @param array $data * @param array $data
* @return Component * @return Component
* @throws ConfigurationException
*/ */
public static function fromArray(array $data): Component public static function fromArray(array $data): Component
{ {
$object = new self(); $name = Functions::array_bc($data, 'name');
$component_data = Functions::array_bc($data, 'data');
$data_type = Functions::array_bc($data, 'data_type') ?? ComponentDataType::PLAIN;
if($name === null)
{
throw new ConfigurationException('The component name is missing');
}
if($component_data === null)
{
throw new ConfigurationException('The component data is missing');
}
$object = new self($name, $component_data, $data_type);
$object->name = Functions::array_bc($data, 'name');
$object->flags = Functions::array_bc($data, 'flags'); $object->flags = Functions::array_bc($data, 'flags');
$object->data_type = Functions::array_bc($data, 'data_type');
$object->checksum = Functions::array_bc($data, 'checksum'); $object->checksum = Functions::array_bc($data, 'checksum');
$object->data = Functions::array_bc($data, 'data');
return $object; return $object;
} }

View file

@ -24,8 +24,10 @@
namespace ncc\Objects\Package; namespace ncc\Objects\Package;
use ncc\Exceptions\ConfigurationException;
use ncc\Interfaces\BytecodeObjectInterface; use ncc\Interfaces\BytecodeObjectInterface;
use ncc\Objects\ProjectConfiguration\ExecutionPolicy; use ncc\Objects\ProjectConfiguration\ExecutionPolicy;
use ncc\Utilities\Base64;
use ncc\Utilities\Functions; use ncc\Utilities\Functions;
class ExecutionUnit implements BytecodeObjectInterface class ExecutionUnit implements BytecodeObjectInterface
@ -49,16 +51,22 @@
*/ */
private $data; private $data;
/**
* @param ExecutionPolicy $execution_policy
* @param string $data
*/
public function __construct(ExecutionPolicy $execution_policy, string $data)
{
$this->execution_policy = $execution_policy;
$this->id = hash('sha1', $this->execution_policy->getName());
$this->data = $data;
}
/** /**
* @return string * @return string
*/ */
public function getId(): string public function getId(): string
{ {
if($this->id === null)
{
$this->id = hash('sha1', $this->execution_policy->getName());
}
return $this->id; return $this->id;
} }
@ -70,14 +78,6 @@
return $this->execution_policy; return $this->execution_policy;
} }
/**
* @param ExecutionPolicy $execution_policy
*/
public function setExecutionPolicy(ExecutionPolicy $execution_policy): void
{
$this->execution_policy = $execution_policy;
}
/** /**
* @return string * @return string
*/ */
@ -86,14 +86,6 @@
return $this->data; return $this->data;
} }
/**
* @param string $data
*/
public function setData(string $data): void
{
$this->data = $data;
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@ -101,7 +93,7 @@
{ {
return [ return [
($bytecode ? Functions::cbc('execution_policy') : 'execution_policy') => $this->execution_policy->toArray($bytecode), ($bytecode ? Functions::cbc('execution_policy') : 'execution_policy') => $this->execution_policy->toArray($bytecode),
($bytecode ? Functions::cbc('data') : 'data') => $this->data, ($bytecode ? Functions::cbc('data') : 'data') => Base64::encode($this->data),
]; ];
} }
@ -110,16 +102,19 @@
*/ */
public static function fromArray(array $data): ExecutionUnit public static function fromArray(array $data): ExecutionUnit
{ {
$object = new self(); $execution_policy = Functions::array_bc($data, 'execution_policy');
$execution_data = Functions::array_bc($data, 'data');
$object->execution_policy = Functions::array_bc($data, 'execution_policy'); if($execution_policy === null)
$object->data = Functions::array_bc($data, 'data');
if($object->execution_policy !== null)
{ {
$object->execution_policy = ExecutionPolicy::fromArray($object->execution_policy); throw new ConfigurationException('Missing execution policy for execution unit');
} }
return $object; if($execution_data === null)
{
throw new ConfigurationException('Missing execution data for execution unit');
}
return new self(ExecutionPolicy::fromArray($execution_policy), Base64::decode($execution_data));
} }
} }

View file

@ -1,210 +0,0 @@
<?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.
*
*/
/** @noinspection PhpMissingFieldTypeInspection */
namespace ncc\Objects\Package;
use ncc\Interfaces\BytecodeObjectInterface;
use ncc\Utilities\Functions;
class Installer implements BytecodeObjectInterface
{
/**
* An array of execution policies to execute pre install
*
* @var string[]|null
*/
private $pre_install;
/**
* An array of execution policies to execute post install
*
* @var string[]|null
*/
private $post_install;
/**
* An array of execution policies to execute pre uninstall
*
* @var string[]|null
*/
private $pre_uninstall;
/**
* An array of execution policies to execute post uninstall
*
* @var string[]|null
*/
private $post_uninstall;
/**
* An array of execution policies to execute pre update
*
* @var string[]|null
*/
private $pre_update;
/**
* An array of execution policies to execute post update
*
* @var string[]|null
*/
private $post_update;
/**
* @return string[]|null
*/
public function getPreInstall(): ?array
{
return $this->pre_install;
}
/**
* @param string[]|null $pre_install
*/
public function setPreInstall(?array $pre_install): void
{
$this->pre_install = $pre_install;
}
/**
* @return string[]|null
*/
public function getPostInstall(): ?array
{
return $this->post_install;
}
/**
* @param string[]|null $post_install
*/
public function setPostInstall(?array $post_install): void
{
$this->post_install = $post_install;
}
/**
* @return string[]|null
*/
public function getPreUninstall(): ?array
{
return $this->pre_uninstall;
}
/**
* @param string[]|null $pre_uninstall
*/
public function setPreUninstall(?array $pre_uninstall): void
{
$this->pre_uninstall = $pre_uninstall;
}
/**
* @return string[]|null
*/
public function getPostUninstall(): ?array
{
return $this->post_uninstall;
}
/**
* @param string[]|null $post_uninstall
*/
public function setPostUninstall(?array $post_uninstall): void
{
$this->post_uninstall = $post_uninstall;
}
/**
* @return string[]|null
*/
public function getPreUpdate(): ?array
{
return $this->pre_update;
}
/**
* @param string[]|null $pre_update
*/
public function setPreUpdate(?array $pre_update): void
{
$this->pre_update = $pre_update;
}
/**
* @return string[]|null
*/
public function getPostUpdate(): ?array
{
return $this->post_update;
}
/**
* @param string[]|null $post_update
*/
public function setPostUpdate(?array $post_update): void
{
$this->post_update = $post_update;
}
/**
* @inheritDoc
*/
public function toArray(bool $bytecode=false): array
{
if(
$this->pre_install === null && $this->post_install === null &&
$this->pre_uninstall === null && $this->post_uninstall === null &&
$this->pre_update === null && $this->post_update === null
)
{
return [];
}
return [
($bytecode ? Functions::cbc('pre_install') : 'pre_install') => $this->pre_install,
($bytecode ? Functions::cbc('post_install') : 'post_install') => $this->post_install,
($bytecode ? Functions::cbc('pre_uninstall') : 'pre_uninstall') => $this->pre_uninstall,
($bytecode? Functions::cbc('post_uninstall') : 'post_uninstall') => $this->post_uninstall,
($bytecode? Functions::cbc('pre_update') : 'pre_update') => $this->pre_update,
($bytecode? Functions::cbc('post_update') : 'post_update') => $this->post_update
];
}
/**
* @inheritDoc
*/
public static function fromArray(array $data): Installer
{
$object = new self();
$object->pre_install = Functions::array_bc($data, 'pre_install');
$object->post_install = Functions::array_bc($data, 'post_install');
$object->pre_uninstall = Functions::array_bc($data, 'pre_uninstall');
$object->post_uninstall = Functions::array_bc($data, 'post_uninstall');
$object->pre_update = Functions::array_bc($data, 'pre_update');
$object->post_update = Functions::array_bc($data, 'post_update');
return $object;
}
}

View file

@ -24,12 +24,13 @@
namespace ncc\Objects\Package; namespace ncc\Objects\Package;
use ncc\Exceptions\ConfigurationException;
use ncc\Interfaces\BytecodeObjectInterface; use ncc\Interfaces\BytecodeObjectInterface;
use ncc\Objects\ProjectConfiguration\Compiler; use ncc\Objects\ProjectConfiguration\Compiler;
use ncc\Objects\ProjectConfiguration\UpdateSource; use ncc\Objects\ProjectConfiguration\UpdateSource;
use ncc\Utilities\Functions; use ncc\Utilities\Functions;
class Header implements BytecodeObjectInterface class Metadata implements BytecodeObjectInterface
{ {
/** /**
* The compiler extension information that was used to build the package * The compiler extension information that was used to build the package
@ -66,12 +67,23 @@
*/ */
private $update_source; private $update_source;
/**
* @var string|null
*/
private $main_execution_policy;
/**
* @var Installer|null
*/
private $installer;
/** /**
* Public Constructor * Public Constructor
*/ */
public function __construct() public function __construct(Compiler $compiler)
{ {
$this->compiler_extension = new Compiler(); $this->compiler_extension = $compiler;
$this->compiler_version = NCC_VERSION_NUMBER;
$this->runtime_constants = []; $this->runtime_constants = [];
$this->options = []; $this->options = [];
} }
@ -184,6 +196,38 @@
$this->update_source = $update_source; $this->update_source = $update_source;
} }
/**
* @return string|null
*/
public function getMainExecutionPolicy(): ?string
{
return $this->main_execution_policy;
}
/**
* @param string|null $main_execution_policy
*/
public function setMainExecutionPolicy(?string $main_execution_policy): void
{
$this->main_execution_policy = $main_execution_policy;
}
/**
* @return Installer|null
*/
public function getInstaller(): ?Installer
{
return $this->installer;
}
/**
* @param Installer|null $installer
*/
public function setInstaller(?Installer $installer): void
{
$this->installer = $installer;
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@ -201,21 +245,21 @@
/** /**
* @inheritDoc * @inheritDoc
*/ */
public static function fromArray(array $data): Header public static function fromArray(array $data): Metadata
{ {
$object = new self(); $compiler_extension = Functions::array_bc($data, 'compiler_extension');
if($compiler_extension === null)
{
throw new ConfigurationException('The compiler extension information is not specified in the package header');
}
$object = new self(Compiler::fromArray($compiler_extension));
$object->compiler_extension = Functions::array_bc($data, 'compiler_extension');
$object->runtime_constants = Functions::array_bc($data, 'runtime_constants'); $object->runtime_constants = Functions::array_bc($data, 'runtime_constants');
$object->compiler_version = Functions::array_bc($data, 'compiler_version'); $object->compiler_version = Functions::array_bc($data, 'compiler_version');
$object->update_source = Functions::array_bc($data, 'update_source'); $object->update_source = Functions::array_bc($data, 'update_source');
$object->options = Functions::array_bc($data, 'options'); $object->options = Functions::array_bc($data, 'options');
if($object->compiler_extension !== null)
{
$object->compiler_extension = Compiler::fromArray($object->compiler_extension);
}
if($object->update_source !== null) if($object->update_source !== null)
{ {
$object->update_source = UpdateSource::fromArray($object->update_source); $object->update_source = UpdateSource::fromArray($object->update_source);

View file

@ -24,7 +24,9 @@
namespace ncc\Objects\Package; namespace ncc\Objects\Package;
use ncc\Exceptions\ConfigurationException;
use ncc\Interfaces\BytecodeObjectInterface; use ncc\Interfaces\BytecodeObjectInterface;
use ncc\Utilities\Base64;
use ncc\Utilities\Functions; use ncc\Utilities\Functions;
class Resource implements BytecodeObjectInterface class Resource implements BytecodeObjectInterface
@ -51,6 +53,13 @@
*/ */
private $data; private $data;
public function __construct(string $name, mixed $data)
{
$this->name = $name;
$this->data = $data;
$this->checksum = hash('sha1', $this->data, true);
}
/** /**
* Validates the checksum of the resource, returns false if the checksum or data is invalid or if the checksum * Validates the checksum of the resource, returns false if the checksum or data is invalid or if the checksum
* failed. * failed.
@ -59,34 +68,9 @@
*/ */
public function validateChecksum(): bool public function validateChecksum(): bool
{ {
if($this->checksum === null)
{
return false;
}
if($this->data === null)
{
return false;
}
return hash_equals($this->checksum, hash('sha1', $this->data, true)); return hash_equals($this->checksum, hash('sha1', $this->data, true));
} }
/**
* Updates the checksum of the resource
*
* @return void
*/
public function updateChecksum(): void
{
$this->checksum = null;
if(is_string($this->data))
{
$this->checksum = hash('sha1', $this->data, true);
}
}
/** /**
* @return string * @return string
*/ */
@ -111,14 +95,6 @@
return $this->checksum; return $this->checksum;
} }
/**
* @param string $checksum
*/
public function setChecksum(string $checksum): void
{
$this->checksum = $checksum;
}
/** /**
* @return string * @return string
*/ */
@ -132,7 +108,8 @@
*/ */
public function setData(string $data): void public function setData(string $data): void
{ {
$this->data = $data; $this->data = Base64::encode($data);
$this->checksum = hash('sha1', $this->data, true);
} }
/** /**
@ -149,14 +126,25 @@
/** /**
* @inheritDoc * @inheritDoc
* @throws ConfigurationException
*/ */
public static function fromArray(array $data): self public static function fromArray(array $data): self
{ {
$object = new self(); $name = Functions::array_bc($data, 'name');
$resource_data = Functions::array_bc($data, 'data');
$object->name = Functions::array_bc($data, 'name'); if($name === null)
{
throw new ConfigurationException('Resource name is not defined');
}
if($resource_data === null)
{
throw new ConfigurationException('Resource data is not defined');
}
$object = new self($name, $resource_data);
$object->checksum = Functions::array_bc($data, 'checksum'); $object->checksum = Functions::array_bc($data, 'checksum');
$object->data = Functions::array_bc($data, 'data');
return $object; return $object;
} }

View file

@ -90,14 +90,14 @@
$package_entry = new PackageEntry(); $package_entry = new PackageEntry();
$package_entry->addVersion($package, $install_path, true); $package_entry->addVersion($package, $install_path, true);
$package_entry->setName($package->getAssembly()->getPackage()); $package_entry->setName($package->getAssembly()->getPackage());
$package_entry->setUpdateSource($package->getHeader()->getUpdateSource()); $package_entry->setUpdateSource($package->getMetadata()->getUpdateSource());
$this->packages[$package->getAssembly()->getPackage()] = $package_entry; $this->packages[$package->getAssembly()->getPackage()] = $package_entry;
$this->update(); $this->update();
return; return;
} }
$this->packages[$package->getAssembly()->getPackage()]->setUpdateSource($package->getHeader()->getUpdateSource()); $this->packages[$package->getAssembly()->getPackage()]->setUpdateSource($package->getMetadata()->getUpdateSource());
$this->packages[$package->getAssembly()->getPackage()]->addVersion($package, $install_path, true); $this->packages[$package->getAssembly()->getPackage()]->addVersion($package, $install_path, true);
$this->update(); $this->update();
} }

View file

@ -171,7 +171,7 @@
$version = new VersionEntry(); $version = new VersionEntry();
$version->setVersion($package->getAssembly()->getVersion()); $version->setVersion($package->getAssembly()->getVersion());
$version->setCompiler($package->getHeader()->getCompilerExtension()); $version->setCompiler($package->getMetadata()->getCompilerExtension());
$version->setExecutionUnits($package->getExecutionUnits()); $version->setExecutionUnits($package->getExecutionUnits());
$version->main_execution_policy = $package->getMainExecutionPolicy(); $version->main_execution_policy = $package->getMainExecutionPolicy();
$version->location = $install_path; $version->location = $install_path;

View file

@ -25,6 +25,7 @@
namespace ncc\Objects; namespace ncc\Objects;
use Exception; use Exception;
use InvalidArgumentException;
use ncc\Enums\Options\BuildConfigurationValues; use ncc\Enums\Options\BuildConfigurationValues;
use ncc\Exceptions\ConfigurationException; use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IOException; use ncc\Exceptions\IOException;
@ -142,9 +143,9 @@
/** /**
* @param string $name * @param string $name
* @return ExecutionPolicy|null * @return ExecutionPolicy
*/ */
private function getExecutionPolicy(string $name): ?ExecutionPolicy public function getExecutionPolicy(string $name): ExecutionPolicy
{ {
foreach($this->execution_policies as $executionPolicy) foreach($this->execution_policies as $executionPolicy)
{ {
@ -154,7 +155,7 @@
} }
} }
return null; throw new InvalidArgumentException('Execution policy \'' . $name . '\' does not exist');
} }
/** /**
@ -203,12 +204,12 @@
* Runs a check on the project configuration and determines what policies are required * Runs a check on the project configuration and determines what policies are required
* *
* @param string $build_configuration * @param string $build_configuration
* @return array * @return string[]
* @throws ConfigurationException * @throws ConfigurationException
*/ */
public function getRequiredExecutionPolicies(string $build_configuration=BuildConfigurationValues::DEFAULT): array public function getRequiredExecutionPolicies(string $build_configuration=BuildConfigurationValues::DEFAULT): array
{ {
if($this->execution_policies === null || count($this->execution_policies) === 0) if(count($this->execution_policies) === 0)
{ {
return []; return [];
} }
@ -220,7 +221,6 @@
foreach($this->execution_policies as $execution_policy) foreach($this->execution_policies as $execution_policy)
{ {
$defined_polices[] = $execution_policy->getName(); $defined_polices[] = $execution_policy->getName();
//$execution_policy->validate();
} }
// Check the installer by batch // Check the installer by batch
@ -249,8 +249,6 @@
} }
} }
if(count($this->build->getPostBuild()) > 0)
{
foreach($this->build->getPostBuild() as $unit) foreach($this->build->getPostBuild() as $unit)
{ {
if(!in_array($unit, $defined_polices, true)) if(!in_array($unit, $defined_polices, true))
@ -263,10 +261,7 @@
$required_policies[] = $unit; $required_policies[] = $unit;
} }
} }
}
if(count($this->build->getPreBuild()) > 0)
{
foreach($this->build->getPreBuild() as $unit) foreach($this->build->getPreBuild() as $unit)
{ {
if(!in_array($unit, $defined_polices, true)) if(!in_array($unit, $defined_polices, true))
@ -279,12 +274,22 @@
$required_policies[] = $unit; $required_policies[] = $unit;
} }
} }
if($this->build->getMain() !== null)
{
if(!in_array($this->build->getMain(), $defined_polices, true))
{
throw new ConfigurationException('The property \'build.main\' in the project configuration calls for an undefined execution policy \'' . $this->build->getMain() . '\'');
} }
/** @noinspection DegradedSwitchInspection */ if(!in_array($this->build->getMain(), $required_policies, true))
switch($build_configuration) {
$required_policies[] = $this->build->getMain();
}
}
if($build_configuration === BuildConfigurationValues::ALL)
{ {
case BuildConfigurationValues::ALL:
/** @var BuildConfiguration $configuration */ /** @var BuildConfiguration $configuration */
foreach($this->build->getBuildConfigurations() as $configuration) foreach($this->build->getBuildConfigurations() as $configuration)
{ {
@ -296,9 +301,9 @@
} }
} }
} }
break; }
else
default: {
$configuration = $this->build->getBuildConfiguration($build_configuration); $configuration = $this->build->getBuildConfiguration($build_configuration);
foreach($this->processBuildPolicies($configuration, $defined_polices) as $policy) foreach($this->processBuildPolicies($configuration, $defined_polices) as $policy)
{ {
@ -307,52 +312,50 @@
$required_policies[] = $policy; $required_policies[] = $policy;
} }
} }
break;
} }
foreach($required_policies as $policy) foreach($required_policies as $policy)
{ {
$execution_policy = $this->getExecutionPolicy($policy); $execution_policy = $this->getExecutionPolicy($policy);
if($execution_policy?->getExitHandlers()->getSuccess()?->getRun() !== null) if($execution_policy?->getExitHandlers()?->getSuccess()?->getRun() !== null)
{ {
if(!in_array($execution_policy?->getExitHandlers()->getSuccess()?->getRun(), $defined_polices, true)) if(!in_array($execution_policy?->getExitHandlers()?->getSuccess()?->getRun(), $defined_polices, true))
{ {
throw new ConfigurationException('The execution policy \'' . $execution_policy?->getName() . '\' Success exit handler points to a undefined execution policy \'' . $execution_policy?->getExitHandlers()->getSuccess()?->getRun() . '\''); throw new ConfigurationException('The execution policy \'' . $execution_policy?->getName() . '\' Success exit handler points to a undefined execution policy \'' . $execution_policy?->getExitHandlers()?->getSuccess()?->getRun() . '\'');
} }
if(!in_array($execution_policy?->getExitHandlers()->getSuccess()?->getRun(), $required_policies, true)) if(!in_array($execution_policy?->getExitHandlers()?->getSuccess()?->getRun(), $required_policies, true))
{ {
$required_policies[] = $execution_policy?->getExitHandlers()->getSuccess()?->getRun(); $required_policies[] = $execution_policy?->getExitHandlers()?->getSuccess()?->getRun();
} }
} }
if($execution_policy?->getExitHandlers()->getWarning()?->getRun() !== null) if($execution_policy?->getExitHandlers()?->getWarning()?->getRun() !== null)
{ {
if(!in_array($execution_policy?->getExitHandlers()->getWarning()?->getRun(), $defined_polices, true)) if(!in_array($execution_policy?->getExitHandlers()?->getWarning()?->getRun(), $defined_polices, true))
{ {
throw new ConfigurationException('The execution policy \'' . $execution_policy?->getName() . '\' Warning exit handler points to a undefined execution policy \'' . $execution_policy?->getExitHandlers()->getWarning()?->getRun() . '\''); throw new ConfigurationException('The execution policy \'' . $execution_policy?->getName() . '\' Warning exit handler points to a undefined execution policy \'' . $execution_policy?->getExitHandlers()?->getWarning()?->getRun() . '\'');
} }
if(!in_array($execution_policy?->getExitHandlers()->getWarning()?->getRun(), $required_policies, true)) if(!in_array($execution_policy?->getExitHandlers()?->getWarning()?->getRun(), $required_policies, true))
{ {
$required_policies[] = $execution_policy?->getExitHandlers()->getWarning()?->getRun(); $required_policies[] = $execution_policy?->getExitHandlers()?->getWarning()?->getRun();
} }
} }
if($execution_policy?->getExitHandlers()->getError()?->getRun() !== null) if($execution_policy?->getExitHandlers()?->getError()?->getRun() !== null)
{ {
if(!in_array($execution_policy?->getExitHandlers()->getError()?->getRun(), $defined_polices, true)) if(!in_array($execution_policy?->getExitHandlers()?->getError()?->getRun(), $defined_polices, true))
{ {
throw new ConfigurationException('The execution policy \'' . $execution_policy?->getName() . '\' Error exit handler points to a undefined execution policy \'' . $execution_policy?->getExitHandlers()->getError()?->getRun() . '\''); throw new ConfigurationException('The execution policy \'' . $execution_policy?->getName() . '\' Error exit handler points to a undefined execution policy \'' . $execution_policy?->getExitHandlers()?->getError()?->getRun() . '\'');
} }
if(!in_array($execution_policy?->getExitHandlers()->getError()?->getRun(), $required_policies, true)) if(!in_array($execution_policy?->getExitHandlers()?->getError()?->getRun(), $required_policies, true))
{ {
$required_policies[] = $execution_policy?->getExitHandlers()->getError()?->getRun(); $required_policies[] = $execution_policy?->getExitHandlers()?->getError()?->getRun();
} }
} }
} }
return $required_policies; return $required_policies;

View file

@ -24,6 +24,7 @@
namespace ncc\Objects\ProjectConfiguration\Build; namespace ncc\Objects\ProjectConfiguration\Build;
use ncc\Enums\BuildOutputType;
use ncc\Exceptions\ConfigurationException; use ncc\Exceptions\ConfigurationException;
use ncc\Interfaces\BytecodeObjectInterface; use ncc\Interfaces\BytecodeObjectInterface;
use ncc\Objects\ProjectConfiguration\Dependency; use ncc\Objects\ProjectConfiguration\Dependency;
@ -43,6 +44,11 @@
*/ */
private $name; private $name;
/**
* @var string
*/
private $build_type;
/** /**
* Options to pass onto the extension compiler * Options to pass onto the extension compiler
* *
@ -99,6 +105,7 @@
public function __construct(string $name, string $output_path) public function __construct(string $name, string $output_path)
{ {
$this->name = $name; $this->name = $name;
$this->build_type = BuildOutputType::NCC_PACKAGE;
$this->output_path = $output_path; $this->output_path = $output_path;
$this->options = []; $this->options = [];
$this->define_constants = []; $this->define_constants = [];
@ -177,6 +184,22 @@
$this->name = $name; $this->name = $name;
} }
/**
* @return string
*/
public function getBuildType(): string
{
return $this->build_type;
}
/**
* @param string $build_type
*/
public function setBuildType(string $build_type): void
{
$this->build_type = $build_type;
}
/** /**
* @return array * @return array
*/ */
@ -370,6 +393,7 @@
$results = []; $results = [];
$results[($bytecode ? Functions::cbc('name') : 'name')] = $this->name; $results[($bytecode ? Functions::cbc('name') : 'name')] = $this->name;
$results[($bytecode ? Functions::cbc('build_type') : 'build_type')] = $this->build_type;
$results[($bytecode ? Functions::cbc('output_path') : 'output_path')] = $this->output_path; $results[($bytecode ? Functions::cbc('output_path') : 'output_path')] = $this->output_path;
if(count($this->options) > 0) if(count($this->options) > 0)
@ -412,6 +436,7 @@
public static function fromArray(array $data): BuildConfiguration public static function fromArray(array $data): BuildConfiguration
{ {
$name = Functions::array_bc($data, 'name'); $name = Functions::array_bc($data, 'name');
$build_type = Functions::array_bc($data, 'build_type');
$output_path = Functions::array_bc($data, 'output_path'); $output_path = Functions::array_bc($data, 'output_path');
if($name === null) if($name === null)

View file

@ -146,17 +146,17 @@
} }
/** /**
* @return ExitHandlers * @return ?ExitHandlers
*/ */
public function getExitHandlers(): ExitHandlers public function getExitHandlers(): ?ExitHandlers
{ {
return $this->exit_handlers; return $this->exit_handlers;
} }
/** /**
* @param ExitHandlers $exit_handlers * @param ExitHandlers|null $exit_handlers
*/ */
public function setExitHandlers(ExitHandlers $exit_handlers): void public function setExitHandlers(?ExitHandlers $exit_handlers): void
{ {
$this->exit_handlers = $exit_handlers; $this->exit_handlers = $exit_handlers;
} }

View file

@ -23,6 +23,7 @@
namespace ncc\Utilities; namespace ncc\Utilities;
use Exception; use Exception;
use FilesystemIterator;
use JsonException; use JsonException;
use ncc\Enums\AuthenticationType; use ncc\Enums\AuthenticationType;
use ncc\Enums\DefinedRemoteSourceType; use ncc\Enums\DefinedRemoteSourceType;
@ -63,6 +64,7 @@
use ncc\ThirdParty\Symfony\Filesystem\Filesystem; use ncc\ThirdParty\Symfony\Filesystem\Filesystem;
use ncc\ThirdParty\Symfony\Process\ExecutableFinder; use ncc\ThirdParty\Symfony\Process\ExecutableFinder;
use ncc\ThirdParty\Symfony\Process\Process; use ncc\ThirdParty\Symfony\Process\Process;
use ncc\ThirdParty\theseer\DirectoryScanner\DirectoryScanner;
use RecursiveDirectoryIterator; use RecursiveDirectoryIterator;
use RecursiveIteratorIterator; use RecursiveIteratorIterator;
use RuntimeException; use RuntimeException;
@ -242,8 +244,7 @@
$banner_version = str_pad($version, 21); $banner_version = str_pad($version, 21);
$banner_copyright = str_pad($copyright, 30); $banner_copyright = str_pad($copyright, 30);
$banner = str_ireplace('%A', $banner_version, $banner); return str_ireplace(array('%A', '%B'), array($banner_version, $banner_copyright), $banner);
return str_ireplace('%B', $banner_copyright, $banner);
} }
/** /**
@ -263,7 +264,7 @@
// Append the trailing slash if it's not already there // Append the trailing slash if it's not already there
// "/etc/foo" becomes "/etc/foo/" // "/etc/foo" becomes "/etc/foo/"
if(substr($base_name, -1) !== DIRECTORY_SEPARATOR) if(!str_ends_with($base_name, DIRECTORY_SEPARATOR))
{ {
$base_name .= DIRECTORY_SEPARATOR; $base_name .= DIRECTORY_SEPARATOR;
} }
@ -280,8 +281,7 @@
*/ */
public static function correctDirectorySeparator($path): string public static function correctDirectorySeparator($path): string
{ {
$path = str_ireplace('/', DIRECTORY_SEPARATOR, $path); return str_ireplace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
return str_ireplace('\\', DIRECTORY_SEPARATOR, $path);
} }
/** /**
@ -805,9 +805,9 @@
// If the specified version is a release, download the source code // If the specified version is a release, download the source code
if($release_results !== null) if($release_results !== null)
{ {
$results->setReleaseName($release_results->getReleaseName() ?? null); $results->setReleaseName($release_results->getReleaseName());
$results->setReleaseDescription($release_results->getReleaseDescription() ?? null); $results->setReleaseDescription($release_results->getReleaseDescription());
$results->setFiles(self::mergeFilesResults($release_results->getFiles(), ($results->getFiles() ?? null))); $results->setFiles(self::mergeFilesResults($release_results->getFiles(), ($results->getFiles())));
if($release_results->getVersion() !== null) if($release_results->getVersion() !== null)
{ {
@ -829,7 +829,7 @@
{ {
if($results->getReleaseName() === null) if($results->getReleaseName() === null)
{ {
$results->setReleaseName($git_results->getReleaseName() ?? null); $results->setReleaseName($git_results->getReleaseName());
} }
elseif($git_results->getReleaseName() !== null) elseif($git_results->getReleaseName() !== null)
{ {
@ -841,7 +841,7 @@
if($results->getReleaseDescription() === null) if($results->getReleaseDescription() === null)
{ {
$results->setReleaseDescription($git_results->getReleaseDescription() ?? null); $results->setReleaseDescription($git_results->getReleaseDescription());
} }
elseif($git_results->getReleaseDescription() !== null) elseif($git_results->getReleaseDescription() !== null)
{ {
@ -853,7 +853,7 @@
if($results->getVersion() === null) if($results->getVersion() === null)
{ {
$results->setVersion($git_results->getVersion() ?? null); $results->setVersion($git_results->getVersion());
} }
elseif($git_results->getVersion() !== null) elseif($git_results->getVersion() !== null)
{ {
@ -864,7 +864,7 @@
} }
} }
$results->setFiles(self::mergeFilesResults($git_results->getFiles(), ($results->getFiles() ?? null))); $results->setFiles(self::mergeFilesResults($git_results->getFiles(), ($results->getFiles())));
} }
try try
@ -881,7 +881,7 @@
{ {
if($results->getReleaseName() === null) if($results->getReleaseName() === null)
{ {
$results->setReleaseName($ncc_package_results->getReleaseName() ?? null); $results->setReleaseName($ncc_package_results->getReleaseName());
} }
elseif($ncc_package_results->getReleaseName() !== null) elseif($ncc_package_results->getReleaseName() !== null)
{ {
@ -893,7 +893,7 @@
if($results->getReleaseDescription() === null) if($results->getReleaseDescription() === null)
{ {
$results->setReleaseDescription($ncc_package_results->getReleaseDescription() ?? null); $results->setReleaseDescription($ncc_package_results->getReleaseDescription());
} }
elseif($ncc_package_results->getReleaseDescription() !== null) elseif($ncc_package_results->getReleaseDescription() !== null)
{ {
@ -905,7 +905,7 @@
if($results->getVersion() === null) if($results->getVersion() === null)
{ {
$results->setVersion($ncc_package_results->getVersion() ?? null); $results->setVersion($ncc_package_results->getVersion());
} }
elseif($ncc_package_results->getVersion() !== null) elseif($ncc_package_results->getVersion() !== null)
{ {
@ -916,7 +916,7 @@
} }
} }
$results->setFiles(self::mergeFilesResults($ncc_package_results->getFiles(), ($results->getFiles() ?? null))); $results->setFiles(self::mergeFilesResults($ncc_package_results->getFiles(), ($results->getFiles())));
} }
return $results; return $results;
@ -1050,7 +1050,7 @@
return RuntimeCache::get('posix_isatty'); return RuntimeCache::get('posix_isatty');
} }
if(function_exists('posix_isatty') === false) if(!function_exists('posix_isatty'))
{ {
return false; return false;
} }
@ -1077,4 +1077,51 @@
return $input; return $input;
} }
/**
* Scans the given directory for files and returns the found file with the given patterns
*
* @param string $path
* @param array $include
* @param array $exclude
* @return array
*/
public static function scanDirectory(string $path, array $include=[], array $exclude=[]): array
{
$directory_scanner = new DirectoryScanner();
try
{
$directory_scanner->unsetFlag(FilesystemIterator::FOLLOW_SYMLINKS);
}
catch (\ncc\ThirdParty\theseer\DirectoryScanner\Exception $e)
{
throw new RuntimeException('Cannot scan directory, unable to remove the FOLLOW_SYMLINKS flag from the iterator: ' . $e->getMessage(), $e->getCode(), $e);
}
if(count($include) > 0)
{
$directory_scanner->setIncludes($include);
}
if(count($exclude) > 0)
{
$directory_scanner->setExcludes($exclude);
}
$results = [];
foreach($directory_scanner($path) as $item)
{
// Ignore directories, they're not important.
if(is_dir($item->getPathName()))
{
continue;
}
$results[] = $item->getPathName();
Console::outVerbose(sprintf('Selected file %s', $item->getPathName()));
}
return $results;
}
} }