- Added new template PhpCliTemplate phpcli

- Added new template PhpLibraryTemplate `phplib`
 - Implemented a template engine and refactored the CLI menu for the Project Manager and added a new `template` command
This commit is contained in:
Netkas 2023-09-01 03:47:01 -04:00
parent 8ea203a0f1
commit 0e8397ec1f
No known key found for this signature in database
GPG key ID: 5DAF58535614062B
23 changed files with 814 additions and 445 deletions

View file

@ -190,7 +190,7 @@
*/
private static function displayVersion(): void
{
Console::out(sprintf('ncc version %s (%s)', ncc_VERSION_NUMBER, ncc_VERSION_BRANCH));
Console::out(sprintf('ncc version %s (%s)', NCC_VERSION_NUMBER, NCC_VERSION_BRANCH));
}
/**

View file

@ -23,15 +23,10 @@
namespace ncc\CLI\Management;
use Exception;
use ncc\Enums\CompilerExtensionDefaultVersions;
use ncc\Enums\CompilerExtensions;
use ncc\Enums\CompilerExtensionSupportedVersions;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\PathNotFoundException;
use ncc\Enums\ProjectTemplates;
use ncc\Managers\ProjectManager;
use ncc\Objects\CliHelpSection;
use ncc\Objects\ProjectConfiguration\Compiler;
use ncc\Objects\ProjectConfiguration;
use ncc\Utilities\Console;
use ncc\Utilities\Functions;
@ -42,185 +37,136 @@
*
* @param $args
* @return void
* @throws ConfigurationException
* @throws IOException
* @throws PathNotFoundException
*/
public static function start($args): void
public static function start(array $args): void
{
if(isset($args['create']))
{
self::createProject($args);
self::initializeProject($args);
return;
}
if(isset($args['template']))
{
self::applyTemplate($args);
return;
}
self::displayOptions();
}
/**
* Creates a new project
* Initializes a new project
*
* @param $args
* @return void
* @throws ConfigurationException
* @throws IOException
* @throws PathNotFoundException
*/
public static function createProject($args): void
private static function initializeProject(array $args): void
{
// First determine the source directory of the project
$current_directory = getcwd();
if(isset($args['src']))
if(isset($args['path']) || isset($args['p']))
{
// Make sure directory separators are corrected
$args['src'] = str_ireplace('/', DIRECTORY_SEPARATOR, $args['src']);
$args['src'] = str_ireplace('\\', DIRECTORY_SEPARATOR, $args['src']);
// Remove the trailing slash
if(substr($args['src'], -1) === DIRECTORY_SEPARATOR)
{
$args['src'] = substr($args['src'], 0, -1);
}
$full_path = getcwd() . DIRECTORY_SEPARATOR . $args['src'];
if(file_exists($full_path) && is_dir($full_path))
{
$real_src = getcwd() . DIRECTORY_SEPARATOR . $args['src'];
}
else
{
Console::outError('The selected source directory \'' . $full_path . '\' was not found or is not a directory', true, 1);
return;
}
$project_path = $args['path'] ?? $args['p'];
}
else
{
$real_src = getcwd() . DIRECTORY_SEPARATOR . 'src';
Console::outError('Missing required option: --path|-p, please specify the path to the project', true, 1);
return;
}
// Remove basename from real_src
$real_src = Functions::removeBasename($real_src, $current_directory);
// Fetch the rest of the information needed for the project
//$compiler_extension = Console::getOptionInput($args, 'ce', 'Compiler Extension (php, java): ');
$package_name = Console::getOptionInput($args, 'package', 'Package Name (com.example.foo): ');
$project_name = Console::getOptionInput($args, 'name', 'Project Name (Foo Bar Library): ');
$Compiler = new Compiler();
// Detect the specified compiler extension
if(isset($args['ext']) || isset($args['extension']))
if(isset($args['name']) || isset($args['n']))
{
$compiler_extension = strtolower(($args['extension'] ?? $args['ext']));
if(in_array($compiler_extension, CompilerExtensions::ALL))
{
$Compiler->setExtension($compiler_extension);
}
else
{
Console::outError('Unsupported extension: ' . $compiler_extension, true, 1);
return;
}
$project_name = $args['name'] ?? $args['n'];
}
else
{
// Default PHP Extension
$Compiler->setExtension(CompilerExtensions::PHP);
Console::outError('Missing required option: --name|-n, please specify the name of the project', true, 1);
return;
}
// If a minimum and maximum version is specified
if(
(isset($args['max-version']) || isset($args['max-ver'])) &&
(isset($args['min-version']) || isset($args['min-ver']))
)
if(isset($args['package']) || isset($args['pkg']))
{
$max_version = strtolower($args['max-version'] ?? $args['max-ver']);
$min_version = strtolower($args['min-version'] ?? $args['min-ver']);
switch($Compiler->getExtension())
{
case CompilerExtensions::PHP:
if(!in_array($max_version, CompilerExtensionSupportedVersions::PHP))
{
Console::outError('The extension \'' . $Compiler->getExtension() . '\' does not support version ' . $max_version, true, 1);
return;
}
if(!in_array($min_version, CompilerExtensionSupportedVersions::PHP))
{
Console::outError('The extension \'' . $Compiler->getExtension() . '\' does not support version ' . $min_version, true, 1);
return;
}
$Compiler->setMaximumVersion($max_version);
$Compiler->setMinimumVersion($min_version);
break;
default:
Console::outError('Unsupported extension: ' . $Compiler->getExtension(), true, 1);
return;
}
$package_name = $args['package'] ?? $args['pkg'];
}
// If a single version is specified
elseif(isset($args['version']) || isset($args['ver']))
{
$version = strtolower($args['version'] ?? $args['ver']);
switch($Compiler->getExtension())
{
case CompilerExtensions::PHP:
if(!in_array($version, CompilerExtensionSupportedVersions::PHP))
{
Console::outError('The extension \'' . $Compiler->getExtension() . '\' does not support version ' . $version, true, 1);
return;
}
$Compiler->setMaximumVersion($version);
$Compiler->setMinimumVersion($version);
break;
default:
Console::outError('Unsupported extension: ' . $Compiler->getExtension(), true, 1);
return;
}
}
// If no version is specified, use the default version
else
{
switch($Compiler->getExtension())
{
case CompilerExtensions::PHP:
$Compiler->setMinimumVersion(CompilerExtensionDefaultVersions::PHP[0]);
$Compiler->setMaximumVersion(CompilerExtensionDefaultVersions::PHP[1]);
break;
default:
Console::outError('Unsupported extension: ' . $Compiler->getExtension(), true, 1);
return;
}
Console::outError('Missing required option: --package|--pkg, please specify the package name of the project', true, 1);
return;
}
// Now create the project
$ProjectManager = new ProjectManager($current_directory);
if(isset($args['ext']))
{
$compiler_extension = $args['ext'];
}
else
{
Console::outError('Missing required option: --ext, please specify the compiler extension of the project', true, 1);
return;
}
try
{
$ProjectManager->initializeProject($Compiler, $project_name, $package_name, $real_src);
}
catch (ConfigurationException $e)
{
Console::outException(sprintf('The project configuration is invalid: %s', $e->getMessage()), $e, 1);
return;
$project_manager = ProjectManager::initializeProject($project_path, $project_name, $package_name, $compiler_extension);
}
catch(Exception $e)
{
Console::outException('There was an unexpected error while trying to initialize the project', $e, 1);
Console::outException('There was an error while trying to initialize the project', $e, 1);
return;
}
Console::out('Project successfully created');
exit(0);
Console::out(sprintf('Project successfully created in \'%s\'', $project_manager->getProjectPath()));
Console::out(sprintf('Modify the project configuration in \'%s\'', $project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'project.json'));
}
private static function applyTemplate(array $args): void
{
if(isset($args['path']) || isset($args['p']))
{
$project_path = $args['path'] ?? $args['p'];
}
else
{
if(is_file(getcwd() . DIRECTORY_SEPARATOR . 'project.json'))
{
$project_path = getcwd();
}
else
{
Console::outError('Missing option: --path|-p, please specify the path to the project', true, 1);
return;
}
}
if(isset($args['name']) || isset($args['n']))
{
$template_name = $args['name'] ?? $args['n'];
}
else
{
Console::outError('Missing required option: --name|-n, please specify the name of the template', true, 1);
return;
}
try
{
$project_manager = new ProjectManager($project_path);
}
catch(Exception $e)
{
Console::outException('There was an error while trying to load the project', $e, 1);
return;
}
try
{
$project_manager->applyTemplate($template_name);
}
catch(Exception $e)
{
Console::outException('There was an error while trying to apply the template', $e, 1);
return;
}
Console::out(sprintf('Template successfully applied to project in \'%s\'', $project_manager->getProjectPath()));
}
/**
@ -232,19 +178,23 @@
{
$options = [
new CliHelpSection(['help'], 'Displays this help menu about the value command'),
new CliHelpSection(['create', '--src', '--package', '--name'], 'Creates a new NCC project'),
new CliHelpSection(['create', '--ext'], 'Specifies the compiler extension'),
new CliHelpSection(['create', '--min-version', '--min-ver', '--maximum-ver', '-max-ver'], 'Specifies the compiler extension version'),
new CliHelpSection(['create-makefile'], 'Generates a Makefile for the project'),
new CliHelpSection(['create', '--path|-p', '--name|-n', '--package|--pkg', '--ext'], 'Creates a new ncc project'),
new CliHelpSection(['template', '--path|-p', '--name|-n'], 'Applies a template to the project'),
];
$options_padding = Functions::detectParametersPadding($options) + 4;
Console::out('Usage: ncc project {command} [options]');
Console::out('Options:' . PHP_EOL);
Console::out('Options:');
foreach($options as $option)
{
Console::out(' ' . $option->toString($options_padding));
}
Console::out(PHP_EOL . 'Available Templates:');
foreach(ProjectTemplates::ALL as $template)
{
Console::out(' ' . $template);
}
}
}

View file

@ -1,37 +1,56 @@
<?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.
*
*/
/*
* 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;
namespace ncc\Classes\NccExtension;
use ncc\Enums\SpecialConstants\BuildConstants;
use ncc\Enums\SpecialConstants\DateTimeConstants;
use ncc\Enums\SpecialConstants\InstallConstants;
use ncc\Enums\SpecialConstants\AssemblyConstants;
use ncc\Enums\SpecialConstants\RuntimeConstants;
use ncc\Managers\ProjectManager;
use ncc\Objects\InstallationPaths;
use ncc\Objects\ProjectConfiguration;
use ncc\Objects\ProjectConfiguration\Assembly;
class ConstantCompiler
{
/**
* Compiles all constants (Usually used during pre-compiling time)
*
* @param ProjectConfiguration $project_configuration
* @param string|null $input
* @return string
*/
public static function compileConstants(ProjectConfiguration $project_configuration, ?string $input): string
{
$input = self::compileAssemblyConstants($input, $project_configuration->getAssembly());
$input = self::compileBuildConstants($input);
$input = self::compileDateTimeConstants($input, time());
$input = self::compileRuntimeConstants($input);
return $input;
}
/**
* Compiles assembly constants about the project (Usually used during compiling time)
*
@ -46,15 +65,19 @@ namespace ncc\Classes\NccExtension;
return null;
}
if($assembly->getName() !== null)
{
$input = str_replace(AssemblyConstants::ASSEMBLY_NAME, $assembly->getName(), $input);
}
if($assembly->getPackage() !== null)
{
$input = str_replace(AssemblyConstants::ASSEMBLY_PACKAGE, $assembly->getPackage(), $input);
}
$input = str_replace(
[
AssemblyConstants::ASSEMBLY_NAME,
AssemblyConstants::ASSEMBLY_PACKAGE,
AssemblyConstants::ASSEMBLY_VERSION,
AssemblyConstants::ASSEMBLY_UID
],
[
$assembly->getName(),
$assembly->getPackage(),
$assembly->getVersion(),
$assembly->getUuid()
], $input);
if($assembly->getDescription() !== null)
{
@ -80,17 +103,6 @@ namespace ncc\Classes\NccExtension;
{
$input =str_replace(AssemblyConstants::ASSEMBLY_TRADEMARK, $assembly->getTrademark(), $input);
}
if($assembly->getVersion() !== null)
{
$input = str_replace(AssemblyConstants::ASSEMBLY_VERSION, $assembly->getVersion(), $input);
}
if($assembly->getUuid() !== null)
{
$input = str_replace(AssemblyConstants::ASSEMBLY_UID, $assembly->getUuid(), $input);
}
return $input;
}
@ -126,10 +138,10 @@ namespace ncc\Classes\NccExtension;
* Compiles installation constants (Usually used during compiling time)
*
* @param string|null $input
* @param InstallationPaths $installationPaths
* @param InstallationPaths $installation_paths
* @return string|null
*/
public static function compileInstallConstants(?string $input, InstallationPaths $installationPaths): ?string
public static function compileInstallConstants(?string $input, InstallationPaths $installation_paths): ?string
{
if($input === null)
{
@ -144,10 +156,10 @@ namespace ncc\Classes\NccExtension;
InstallConstants::INSTALL_PATH_DATA
],
[
$installationPaths->getInstallationpath(),
$installationPaths->getBinPath(),
$installationPaths->getSourcePath(),
$installationPaths->getDataPath()
$installation_paths->getInstallationpath(),
$installation_paths->getBinPath(),
$installation_paths->getSourcePath(),
$installation_paths->getDataPath()
], $input);
}

View file

@ -0,0 +1,114 @@
<?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 ncc\Classes\NccExtension\ConstantCompiler;
use ncc\Enums\Options\ProjectOptions;
use ncc\Enums\Runners;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\PathNotFoundException;
use ncc\Interfaces\TemplateInterface;
use ncc\Managers\ProjectManager;
use ncc\Objects\ProjectConfiguration\ExecutionPolicy;
use ncc\Utilities\IO;
class PhpCliTemplate implements TemplateInterface
{
/**
* @inheritDoc
* @param ProjectManager $project_manager
* @throws IOException
* @throws PathNotFoundException
* @throws ConfigurationException
*/
public static function applyTemplate(ProjectManager $project_manager): void
{
$project_manager->getProjectConfiguration()->addExecutionPolicy(
new ExecutionPolicy('main_policy', Runners::PHP, new ExecutionPolicy\Execute('main'))
);
$project_manager->getProjectConfiguration()->getBuild()->setMain('main_policy');
$project_manager->getProjectConfiguration()->getProject()->addOption(ProjectOptions::CREATE_SYMLINK, true);
self::writeProgramTemplate($project_manager);
self::writeMainEntryTemplate($project_manager);
self::writeMakefileTemplate($project_manager);
$project_manager->save();
}
/**
* Writes the Program.php file to the project source directory
*
* @param ProjectManager $project_manager
* @return void
* @throws IOException
* @throws PathNotFoundException
*/
private static function writeProgramTemplate(ProjectManager $project_manager): void
{
IO::fwrite(
$project_manager->getProjectSourcePath() . DIRECTORY_SEPARATOR . 'Program.php',
ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(),
IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'TemplateFiles' . DIRECTORY_SEPARATOR . 'Program.php.tpl')
)
);
}
/**
* Writes the main.php file to the project directory
*
* @param ProjectManager $project_manager
* @return void
* @throws IOException
* @throws PathNotFoundException
*/
private static function writeMainEntryTemplate(ProjectManager $project_manager): void
{
IO::fwrite(
$project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'main',
ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(),
IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'TemplateFiles' . DIRECTORY_SEPARATOR . 'main.php.tpl')
)
);
}
/**
* Writes the Makefile to the project directory
*
* @param ProjectManager $project_manager
* @return void
* @throws IOException
* @throws PathNotFoundException
*/
private static function writeMakefileTemplate(ProjectManager $project_manager): void
{
IO::fwrite(
$project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'Makefile',
ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(),
IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'TemplateFiles' . DIRECTORY_SEPARATOR . 'Makefile.tpl')
)
);
}
}

View file

@ -0,0 +1,76 @@
<?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 ncc\Classes\NccExtension\ConstantCompiler;
use ncc\Exceptions\IOException;
use ncc\Exceptions\PathNotFoundException;
use ncc\Interfaces\TemplateInterface;
use ncc\Managers\ProjectManager;
use ncc\Utilities\IO;
class PhpLibraryTemplate implements TemplateInterface
{
/**
* @inheritDoc
*/
public static function applyTemplate(ProjectManager $project_manager): void
{
self::createClassTemplate($project_manager);
self::writeMakefileTemplate($project_manager);
}
/**
* @param ProjectManager $project_manager
* @return void
* @throws IOException
* @throws PathNotFoundException
*/
private static function createClassTemplate(ProjectManager $project_manager): void
{
IO::fwrite(
$project_manager->getProjectSourcePath() . DIRECTORY_SEPARATOR . $project_manager->getProjectConfiguration()->getAssembly()->getName() . '.php',
ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(),
IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'TemplateFiles' . DIRECTORY_SEPARATOR . 'class.php.tpl')
)
);
}
/**
* Writes the Makefile to the project directory
*
* @param ProjectManager $project_manager
* @return void
* @throws IOException
* @throws PathNotFoundException
*/
private static function writeMakefileTemplate(ProjectManager $project_manager): void
{
IO::fwrite(
$project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'Makefile',
ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(),
IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'TemplateFiles' . DIRECTORY_SEPARATOR . 'Makefile.tpl')
)
);
}
}

View file

@ -0,0 +1,20 @@
# Variables
CONFIG ?= release
LOG_LEVEL = debug
OUTDIR = build/$(CONFIG)
PACKAGE = $(OUTDIR)/%ASSEMBLY.PACKAGE%.ncc
# Default Target
all: build
# Build Steps
build:
ncc build --config=$(CONFIG) --log-level $(LOG_LEVEL)
install:
ncc package install --package=$(PACKAGE) --skip-dependencies --reinstall -y --log-level $(LOG_LEVEL)
clean:
rm -rf build
.PHONY: all build install clean

View file

@ -0,0 +1,17 @@
<?php
namespace %ASSEMBLY.NAME%;
class Program
{
/**
* %ASSEMBLY.NAME% main entry point
*
* @param string[] $args
* @return void
*/
public static function main(array $args): void
{
print("Hello World from %ASSEMBLY.PACKAGE%!" . PHP_EOL);
}
}

View file

@ -0,0 +1,8 @@
<?php
namespace %ASSEMBLY.NAME%;
class %ASSEMBLY.NAME%
{
}

View file

@ -0,0 +1,17 @@
<?php
if (PHP_SAPI !== 'cli')
{
print('%ASSEMBLY.PACKAGE% must be run from the command line.' . PHP_EOL);
exit(1);
}
if(!isset($argv))
{
trigger_error('No $argv found, maybe you are using php-cgi?', E_USER_ERROR);
}
require('ncc');
import('%ASSEMBLY.PACKAGE%', 'latest');
\%ASSEMBLY.NAME%\Program::main($argv);

View file

@ -24,5 +24,13 @@
final class InitializeProjectOptions
{
public const CREATE_SOURCE_DIRECTORY = 1;
/**
* A custom path to the project's source directory
*/
public const PROJECT_SRC_PATH = 'PROJECT_SRC_PATH';
/**
* A boolean option that indicates whether to overwrite the project file if it already exists
*/
public const OVERWRITE_PROJECT_FILE = 'OVERWRITE_PROJECT_FILE';
}

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\Options;
final class ProjectOptions
{
public const CREATE_SYMLINK = 'create_symlink';
}

View file

@ -0,0 +1,44 @@
<?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 ProjectTemplates
{
/**
* A template that is used to create a PHP library project
*/
public const PHP_LIBRARY = 'phplib';
/**
* A template that is used to create a PHP CLI application project
*/
public const PHP_CLI = 'phpcli';
/**
* An array of all the available project templates
*/
public const ALL = [
self::PHP_LIBRARY,
self::PHP_CLI
];
}

View file

@ -0,0 +1,36 @@
<?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\Interfaces;
use ncc\Managers\ProjectManager;
interface TemplateInterface
{
/**
* Applies the template to the project
*
* @param ProjectManager $project_manager
* @return void
*/
public static function applyTemplate(ProjectManager $project_manager): void;
}

View file

@ -502,9 +502,9 @@
}
else
{
$this->handleExit($package, $version, $unit->getExecutionPolicy()->getExitHandlers()->getSuccess(), $process);
$this->handleExit($package, $version, $unit->getExecutionPolicy()->getExitHandlers()->getWarning(), $process);
$this->handleExit($package, $version, $unit->executigetExecutionPolicy()on_policy->getExitHandlers()->getError(), $process);
$this->handleExit($package, $version, $unit->getExecutionPolicy()->getExitHandlers()?->getSuccess(), $process);
$this->handleExit($package, $version, $unit->getExecutionPolicy()->getExitHandlers()?->getWarning(), $process);
$this->handleExit($package, $version, $unit->getExecutionPolicy()->getExitHandlers()?->getError(), $process);
}
}

View file

@ -25,9 +25,13 @@
namespace ncc\Managers;
use JetBrains\PhpStorm\NoReturn;
use ncc\Classes\PhpExtension\PhpCliTemplate;
use ncc\Classes\PhpExtension\PhpLibraryTemplate;
use ncc\Enums\Options\BuildConfigurationValues;
use ncc\Enums\Options\InitializeProjectOptions;
use ncc\Classes\NccExtension\PackageCompiler;
use ncc\Enums\ProjectTemplates;
use ncc\Exceptions\BuildException;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\IOException;
@ -35,9 +39,8 @@
use ncc\Exceptions\PathNotFoundException;
use ncc\Objects\ProjectConfiguration;
use ncc\Objects\ProjectConfiguration\Compiler;
use ncc\ThirdParty\Symfony\Uid\Uuid;
use ncc\Utilities\Console;
use ncc\Utilities\Validate;
use RuntimeException;
class ProjectManager
{
@ -58,7 +61,7 @@
/**
* The loaded project configuration, null if no project file is loaded
*
* @var ProjectConfiguration|null
* @var ProjectConfiguration
*/
private $project_configuration;
@ -69,13 +72,14 @@
* @throws ConfigurationException
* @throws IOException
* @throws PathNotFoundException
* @throws NotSupportedException
*/
public function __construct(string $path)
{
// Auto-resolve the trailing slash
if(!str_ends_with($path, '/'))
if(str_ends_with($path, '/'))
{
$path .= DIRECTORY_SEPARATOR;
$path = substr($path, 0, -1);
}
// Detect if the folder exists or not
@ -85,139 +89,7 @@
}
$this->project_path = $path;
$this->project_file_path = $path . 'project.json';
if(file_exists($this->project_file_path))
{
$this->load();
}
}
/**
* Initializes the project structure
*
* @param Compiler $compiler
* @param string $name
* @param string $package
* @param string|null $src
* @param array $options
* @throws ConfigurationException
* @throws IOException
*/
public function initializeProject(Compiler $compiler, string $name, string $package, ?string $src=null, array $options=[]): void
{
// Validate the project information first
if(!Validate::packageName($package))
{
throw new ConfigurationException('The given package name \'' . $package . '\' is not a valid package name');
}
if(!Validate::projectName($name))
{
throw new ConfigurationException('The given project name \'' . $name . '\' is not valid');
}
if(file_exists($this->project_path . DIRECTORY_SEPARATOR . 'project.json'))
{
throw new IOException('A project has already been initialized in \'' . $this->project_path . DIRECTORY_SEPARATOR . 'project.json' . '\'');
}
$this->project_configuration = new ProjectConfiguration();
// Set the compiler information
$this->project_configuration->getProject()->setCompiler($compiler);
// Set the assembly information
$this->project_configuration->getAssembly()->setName($name);
$this->project_configuration->getAssembly()->setPackage($package);
$this->project_configuration->getAssembly()->setVersion('1.0.0');
$this->project_configuration->getAssembly()->setUuid(Uuid::v1()->toRfc4122());
// Set the build information
$this->project_configuration->getBuild()->setSourcePath($src);
if($this->project_configuration->getBuild()->getSourcePath() === null)
{
$this->project_configuration->getBuild()->setSourcePath($this->project_path);
}
$this->project_configuration->getBuild()->setDefaultConfiguration('debug');
// Assembly constants if the program wishes to check for this
$this->project_configuration->getBuild()->addDefineConstant('ASSEMBLY_PACKAGE', '%ASSEMBLY.PACKAGE%');
$this->project_configuration->getBuild()->addDefineConstant('ASSEMBLY_VERSION', '%ASSEMBLY.VERSION%');
$this->project_configuration->getBuild()->addDefineConstant('ASSEMBLY_UID', '%ASSEMBLY.UID%');
// Generate configurations
$debug_configuration = new ProjectConfiguration\Build\BuildConfiguration();
$debug_configuration->setName('debug');
$debug_configuration->setOutputPath('build/debug');
$debug_configuration->setDefinedConstant('DEBUG', '1'); // Debugging constant if the program wishes to check for this
$this->project_configuration->getBuild()->addBuildConfiguration($debug_configuration);
$release_configuration = new ProjectConfiguration\Build\BuildConfiguration();
$release_configuration->setName('release');
$release_configuration->setOutputPath('build/release');
$release_configuration->setDefinedConstant('DEBUG', '0'); // Debugging constant if the program wishes to check for this
$this->project_configuration->getBuild()->addBuildConfiguration($release_configuration);
// Finally, create project.json
$this->project_configuration->toFile($this->project_path . DIRECTORY_SEPARATOR . 'project.json');
// And create the project directory for additional assets/resources
$Folders = [
$this->project_path . DIRECTORY_SEPARATOR . 'ncc',
$this->project_path . DIRECTORY_SEPARATOR . 'ncc' . DIRECTORY_SEPARATOR . 'cache',
$this->project_path . DIRECTORY_SEPARATOR . 'ncc' . DIRECTORY_SEPARATOR . 'config',
];
foreach($Folders as $folder)
{
if(!file_exists($folder) && !mkdir($folder) && !is_dir($folder))
{
throw new RuntimeException(sprintf('Directory "%s" was not created', $folder));
}
}
// Process options
foreach($options as $option)
{
if (
$option === InitializeProjectOptions::CREATE_SOURCE_DIRECTORY &&
!file_exists($this->project_configuration->getBuild()->getSourcePath()) &&
!mkdir($concurrentDirectory = $this->project_configuration->getBuild()->getSourcePath()) &&
!is_dir($concurrentDirectory)
)
{
throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
}
}
}
/**
* Determines if a project configuration is loaded or not
*
* @return bool
*/
public function projectLoaded(): bool
{
return $this->project_configuration !== null;
}
/**
* Attempts to load the project configuration
*
* @return void
* @throws IOException
* @throws PathNotFoundException
*/
public function load(): void
{
if(!is_file($this->project_file_path))
{
throw new PathNotFoundException($this->project_file_path);
}
$this->project_file_path = $this->project_path . DIRECTORY_SEPARATOR . 'project.json';
$this->project_configuration = ProjectConfiguration::fromFile($this->project_file_path);
}
@ -229,11 +101,6 @@
*/
public function save(): void
{
if(!$this->projectLoaded())
{
return;
}
$this->project_configuration->toFile($this->project_file_path);
}
@ -241,17 +108,9 @@
* Returns the ProjectConfiguration object
*
* @return ProjectConfiguration
* @throws ConfigurationException
* @throws IOException
* @throws PathNotFoundException
*/
public function getProjectConfiguration(): ProjectConfiguration
{
if($this->project_configuration === null)
{
$this->load();
}
return $this->project_configuration;
}
@ -265,6 +124,15 @@
return $this->project_path;
}
/**
* Returns the project's source path
*
* @return string|null
*/
public function getProjectSourcePath(): ?string
{
return $this->project_path . DIRECTORY_SEPARATOR . $this->project_configuration->getBuild()->getSourcePath();
}
/**
* Compiles the project into a package
@ -281,4 +149,103 @@
{
return PackageCompiler::compile($this, $build_configuration);
}
/**
* Applies the given template to the project
*
* @param string $template_name
* @return void
* @throws ConfigurationException
* @throws IOException
* @throws NotSupportedException
* @throws PathNotFoundException
*/
public function applyTemplate(string $template_name): void
{
switch(strtolower($template_name))
{
case ProjectTemplates::PHP_CLI:
PhpCliTemplate::applyTemplate($this);
break;
case ProjectTemplates::PHP_LIBRARY:
PhpLibraryTemplate::applyTemplate($this);
break;
default:
throw new NotSupportedException('The given template \'' . $template_name . '\' is not supported');
}
}
/**
* Initializes the project structure
*
* @param string $project_path The directory for the project to be initialized in
* @param string $name The name of the project eg; ProjectLib
* @param string $package The standard package name eg; com.example.project
* @param Compiler $compiler The compiler to use for this project
* @param array $options An array of options to use when initializing the project
* @return ProjectManager
* @throws ConfigurationException
* @throws IOException
* @throws NotSupportedException
* @throws PathNotFoundException
*/
public static function initializeProject(string $project_path, string $name, string $package, string $compiler, array $options=[]): ProjectManager
{
if(str_ends_with($project_path, DIRECTORY_SEPARATOR))
{
$project_path = substr($project_path, 0, -1);
}
if(is_file($project_path . DIRECTORY_SEPARATOR . 'project.json'))
{
if(!isset($options[InitializeProjectOptions::OVERWRITE_PROJECT_FILE]))
{
throw new IOException('A project has already been initialized in \'' . $project_path . DIRECTORY_SEPARATOR . 'project.json' . '\'');
}
Console::out(sprintf('Overwriting project.json in \'%s\'', $project_path));
unlink($project_path . DIRECTORY_SEPARATOR . 'project.json');
}
$project_src = $options[InitializeProjectOptions::PROJECT_SRC_PATH] ?? ('src' . DIRECTORY_SEPARATOR . $name);
if(str_ends_with($project_src, DIRECTORY_SEPARATOR))
{
$project_src = substr($project_src, 0, -1);
}
if(!mkdir($project_path, 0777, true) && !is_dir($project_path))
{
throw new IOException(sprintf('Project directory "%s" was not created', $project_path));
}
if(!mkdir($project_path . DIRECTORY_SEPARATOR . $project_src, 0777, true) && !is_dir($project_path . DIRECTORY_SEPARATOR . $project_src))
{
throw new IOException(sprintf('Project source directory "%s" was not created', $project_path . DIRECTORY_SEPARATOR . $project_src));
}
// Create the build configuration
$build = new ProjectConfiguration\Build($project_src);
$build->addDefineConstant('ASSEMBLY_PACKAGE', '%ASSEMBLY.PACKAGE%');
$build->addDefineConstant('ASSEMBLY_VERSION', '%ASSEMBLY.VERSION%');
$build->addDefineConstant('ASSEMBLY_UID', '%ASSEMBLY.UID%');
// Generate the Debug & Release build configurations
$debug_configuration = new ProjectConfiguration\Build\BuildConfiguration('debug', 'build' . DIRECTORY_SEPARATOR . 'debug');
$debug_configuration->setDefinedConstant('DEBUG', '1');
$build->addBuildConfiguration(new ProjectConfiguration\Build\BuildConfiguration('release', 'build' . DIRECTORY_SEPARATOR . 'release'));
$build->addBuildConfiguration($debug_configuration);
$build->setDefaultConfiguration('release');
$project_configuration = new ProjectConfiguration(
new ProjectConfiguration\Project($compiler),
new ProjectConfiguration\Assembly($name, $package),
$build
);
// Finally, create project.json and return a new ProjectManager
$project_configuration->toFile($project_path . DIRECTORY_SEPARATOR . 'project.json');
return new ProjectManager($project_path);
}
}

View file

@ -140,77 +140,6 @@
$this->execution_policies = $execution_policies;
}
/**
* @return Installer|null
*/
public function getInstaller(): ?Installer
{
return $this->installer;
}
/**
* @param Installer|null $installer
*/
public function setInstaller(?Installer $installer): void
{
$this->installer = $installer;
}
/**
* @return Build
*/
public function getBuild(): Build
{
return $this->build;
}
/**
* @param Build $build
*/
public function setBuild(Build $build): void
{
$this->build = $build;
}
/**
* @inheritDoc
*/
public function validate(): void
{
$this->project->validate();
$this->assembly->validate();
$this->build->validate();
if($this->build->getMain() !== null)
{
if($this->execution_policies === null || count($this->execution_policies) === 0)
{
throw new ConfigurationException(sprintf('Build configuration build.main uses an execution policy "%s" but no policies are defined', $this->build->getMain()));
}
$found = false;
foreach($this->execution_policies as $policy)
{
if($policy->getName() === $this->build->getMain())
{
$found = true;
break;
}
}
if(!$found)
{
throw new ConfigurationException(sprintf('Build configuration build.main points to a undefined execution policy "%s"', $this->build->getMain()));
}
if($this->build->getMain() === BuildConfigurationValues::ALL)
{
throw new ConfigurationException(sprintf('Build configuration build.main cannot be set to "%s"', BuildConfigurationValues::ALL));
}
}
}
/**
* @param string $name
* @return ExecutionPolicy|null
@ -228,6 +157,44 @@
return null;
}
/**
* @param ExecutionPolicy $policy
* @param bool $overwrite
* @return void
* @throws ConfigurationException
*/
public function addExecutionPolicy(ExecutionPolicy $policy, bool $overwrite=true): void
{
if(!$overwrite)
{
foreach($this->execution_policies as $executionPolicy)
{
if($executionPolicy->getName() === $policy->getName())
{
throw new ConfigurationException('An execution policy with the name \'' . $policy->getName() . '\' already exists');
}
}
}
$this->execution_policies[] = $policy;
}
/**
* @param string $name
* @return void
*/
public function removeExecutionPolicy(string $name): void
{
foreach($this->execution_policies as $key => $executionPolicy)
{
if($executionPolicy->getName() === $name)
{
unset($this->execution_policies[$key]);
return;
}
}
}
/**
* Runs a check on the project configuration and determines what policies are required
*
@ -387,6 +354,77 @@
return $required_policies;
}
/**
* @return Installer|null
*/
public function getInstaller(): ?Installer
{
return $this->installer;
}
/**
* @param Installer|null $installer
*/
public function setInstaller(?Installer $installer): void
{
$this->installer = $installer;
}
/**
* @return Build
*/
public function getBuild(): Build
{
return $this->build;
}
/**
* @param Build $build
*/
public function setBuild(Build $build): void
{
$this->build = $build;
}
/**
* @inheritDoc
*/
public function validate(): void
{
$this->project->validate();
$this->assembly->validate();
$this->build->validate();
if($this->build->getMain() !== null)
{
if($this->execution_policies === null || count($this->execution_policies) === 0)
{
throw new ConfigurationException(sprintf('Build configuration build.main uses an execution policy "%s" but no policies are defined', $this->build->getMain()));
}
$found = false;
foreach($this->execution_policies as $policy)
{
if($policy->getName() === $this->build->getMain())
{
$found = true;
break;
}
}
if(!$found)
{
throw new ConfigurationException(sprintf('Build configuration build.main points to a undefined execution policy "%s"', $this->build->getMain()));
}
if($this->build->getMain() === BuildConfigurationValues::ALL)
{
throw new ConfigurationException(sprintf('Build configuration build.main cannot be set to "%s"', BuildConfigurationValues::ALL));
}
}
}
/**
* Writes a json representation of the object to a file
*
@ -493,7 +531,7 @@
foreach($this->execution_policies as $executionPolicy)
{
$execution_policies[$executionPolicy->getName()] = $executionPolicy->toArray($bytecode);
$execution_policies[] = $executionPolicy->toArray($bytecode);
}
$results[($bytecode ? Functions::cbc('execution_policies') : 'execution_policies')] = $execution_policies;

View file

@ -24,6 +24,7 @@
namespace ncc\Objects\ProjectConfiguration;
use ncc\Exceptions\ConfigurationException;
use ncc\Interfaces\BytecodeObjectInterface;
use ncc\Objects\ProjectConfiguration\ExecutionPolicy\Execute;
use ncc\Objects\ProjectConfiguration\ExecutionPolicy\ExitHandlers;
@ -66,6 +67,20 @@
*/
private $message;
/**
* ExecutionPolicy constructor.
*
* @param string $name
* @param string $runner
* @param Execute $execute
*/
public function __construct(string $name, string $runner, Execute $execute)
{
$this->name = $name;
$this->runner = $runner;
$this->execute = $execute;
}
/**
* @return string
*/
@ -192,22 +207,34 @@
/**
* @inheritDoc
* @throws ConfigurationException
*/
public static function fromArray(array $data): ExecutionPolicy
{
$object = new self();
$name = Functions::array_bc($data, 'name');
$runner = Functions::array_bc($data, 'runner');
$execute = Functions::array_bc($data, 'execute');
$object->name = Functions::array_bc($data, 'name');
$object->runner = Functions::array_bc($data, 'runner');
$object->message = Functions::array_bc($data, 'message');
$object->execute = Functions::array_bc($data, 'execute');
$object->exit_handlers = Functions::array_bc($data, 'exit_handlers');
if($object->execute !== null)
if($name === null || $name === '')
{
$object->execute = Execute::fromArray($object->execute);
throw new ConfigurationException('ExecutionPolicy name cannot be null or empty');
}
if($runner === null || $runner === '')
{
throw new ConfigurationException('ExecutionPolicy runner cannot be null or empty');
}
if($execute === null)
{
throw new ConfigurationException('ExecutionPolicy execute cannot be null');
}
$object = new self($name, $runner, Execute::fromArray($execute));
$object->message = Functions::array_bc($data, 'message');
$object->exit_handlers = Functions::array_bc($data, 'exit_handlers');
if($object->exit_handlers !== null)
{
$object->exit_handlers = ExitHandlers::fromArray($object->exit_handlers);

View file

@ -88,9 +88,12 @@
private $idle_timeout;
/**
* Public Constructor
* Execute constructor.
*
* @param string $target
* @param string|null $working_directory
*/
public function __construct(string $target, ?string $working_directory)
public function __construct(string $target, ?string $working_directory=null)
{
$this->target = $target;
$this->working_directory = $working_directory ?? RuntimeConstants::CWD;

View file

@ -52,9 +52,16 @@
/**
* Public Constructor
* @param string|Compiler $compiler
* @throws NotSupportedException
*/
public function __construct(Compiler $compiler)
public function __construct(string|Compiler $compiler)
{
if(is_string($compiler))
{
$compiler = new Compiler($compiler);
}
$this->compiler = $compiler;
$this->options = [];
}