extends this interface to allow for serialization of compiled assets - Updated class `\ncc\Objects\ComposerJson > Author` to use method calls rather than direct property access and implemented `SerializableObjectInterface` - Updated class `\ncc\Objects\ComposerJson > Autoloader` to use method calls rather than direct property access and implemented `SerializableObjectInterface` - Updated class `\ncc\Objects\ComposerJson > Funding` to use method calls rather than direct property access and implemented `SerializableObjectInterface` - Updated class `\ncc\Objects\ComposerJson > NamespacePointer` to use method calls rather than direct property access and implemented `SerializableObjectInterface` - Updated class `\ncc\Objects\ComposerJson > PackageLink` to use method calls rather than direct property access and implemented `SerializableObjectInterface` - Updated class `\ncc\Objects\ComposerJson > Suggestion` to use method calls rather than direct property access and implemented `SerializableObjectInterface` - Updated class `\ncc\Objects\ComposerJson > Support` to use method calls rather than direct property access and implemented `SerializableObjectInterface` - Updated class `\ncc\Objects\ExecutionPointers > ExecutionPointer` to use method calls rather than direct property access and implemented `BytecodeObjectInterface` - Updated class `\ncc\Objects\NccVersionInformation > Component` to use method calls rather than direct property access and implemented `SerializableObjectInterface` - Updated class `\ncc\Objects\Package > Component` to use method calls rather than direct property access and implemented `BytecodeObjectInterface` - Updated class `\ncc\Objects\Package > ExecutionUnit` to use method calls rather than direct property access and implemented `BytecodeObjectInterface` - Updated class `\ncc\Objects\Package > Header` to use method calls rather than direct property access and implemented `BytecodeObjectInterface` - Updated class `\ncc\Objects\Package > Installer` to use method calls rather than direct property access and implemented `BytecodeObjectInterface` - Updated class `\ncc\Objects\Package > MagicBytes` to use method calls rather than direct property access and implemented `SerializableObjectInterface` - Updated class `\ncc\Objects\Package > Resource` to use method calls rather than direct property access and implemented `BytecodeObjectInterface` - Updated class `\ncc\Objects\PackageLock > DependencyEntry` to use method calls rather than direct property access and implemented `BytecodeObjectInterface` - Updated class `\ncc\Objects\PackageLock > PackageEntry` to use method calls rather than direct property access and implemented `BytecodeObjectInterface` - Updated class `\ncc\Objects\PackageLock > VersionEntry` to use method calls rather than direct property access and implemented `BytecodeObjectInterface`
405 lines
No EOL
16 KiB
PHP
405 lines
No EOL
16 KiB
PHP
<?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(Main::getLogLevel() !== null && Resolver::checkLogLevel(LogLevel::DEBUG, Main::getLogLevel()))
|
|
{
|
|
foreach($configuration->assembly->toArray() as $prop => $value)
|
|
{
|
|
Console::outDebug(sprintf('assembly.%s: %s', $prop, ($value ?? 'n/a')));
|
|
}
|
|
foreach($configuration->project->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->project->getCompiler()->getExtension()) === CompilerExtensions::PHP)
|
|
{
|
|
/** @var CompilerInterface $Compiler */
|
|
$Compiler = new PhpCompiler($configuration, $manager->getProjectPath());
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException('The compiler extension \'' . $configuration->project->getCompiler()->getExtension() . '\' is not supported');
|
|
}
|
|
|
|
$build_configuration = $configuration->build->getBuildConfiguration($build_configuration)->getName();
|
|
Console::out(sprintf('Building %s=%s', $configuration->assembly->getPackage(), $configuration->assembly->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->ProjectType === ProjectType::COMPOSER)
|
|
{
|
|
$project_path = ComposerSourceBuiltin::fromLocal($project_type->ProjectPath);
|
|
}
|
|
elseif($project_type->ProjectType === ProjectType::NCC)
|
|
{
|
|
$project_manager = new ProjectManager($project_type->ProjectPath);
|
|
$project_manager->getProjectConfiguration()->assembly->setVersion($version);
|
|
$project_path = $project_manager->build();
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException(sprintf('Failed to compile %s, project type %s is not supported', $project_type->ProjectPath, $project_type->ProjectType));
|
|
}
|
|
|
|
if($version !== null)
|
|
{
|
|
$package = Package::load($project_path);
|
|
$package->assembly->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->execution_policies) === 0)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
Console::out('Compiling Execution Policies');
|
|
$total_items = count($configuration->execution_policies);
|
|
$execution_units = [];
|
|
$processed_items = 1;
|
|
|
|
/** @var ProjectConfiguration\ExecutionPolicy $policy */
|
|
foreach($configuration->execution_policies 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->build->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->assembly->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->assembly !== null)
|
|
{
|
|
$assembly = [];
|
|
|
|
foreach($package->assembly->toArray() as $key => $value)
|
|
{
|
|
Console::outDebug(sprintf('compiling constant Assembly.%s (%s)', $key, implode(', ', array_keys($refs))));
|
|
$assembly[$key] = self::compileConstants($value, $refs);
|
|
}
|
|
$package->assembly = Assembly::fromArray($assembly);
|
|
|
|
unset($assembly);
|
|
}
|
|
|
|
if($package->execution_units !== null && count($package->execution_units) > 0)
|
|
{
|
|
$units = [];
|
|
foreach($package->execution_units as $executionUnit)
|
|
{
|
|
Console::outDebug(sprintf('compiling execution unit constant %s (%s)', $executionUnit->getExecutionPolicy()->getName(), implode(', ', array_keys($refs))));
|
|
$units[] = self::compileExecutionUnitConstants($executionUnit, $refs);
|
|
}
|
|
$package->execution_units = $units;
|
|
unset($units);
|
|
}
|
|
|
|
$compiled_constants = [];
|
|
foreach($package->header->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->header->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->header->setOptions($options);
|
|
$package->header->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;
|
|
}
|
|
} |