Progress made on the compiler, added example library for compiling tests

This commit is contained in:
Netkas 2022-10-18 20:47:03 -04:00
parent 56b2d52459
commit 1e049c5187
19 changed files with 94317 additions and 10 deletions

View file

@ -0,0 +1,13 @@
<?php
namespace ncc\Abstracts;
abstract class ComponentFileExtensions
{
/**
* The file extensions that the PHP compiler extension will accept as components.
*
* @var array
*/
const Php = ['*.php', '*.php3', '*.php4', '*.php5', '*.phtml'];
}

View file

@ -4,6 +4,7 @@
use ncc\Exceptions\AccessDeniedException;
use ncc\Exceptions\AutoloadGeneratorException;
use ncc\Exceptions\BuildConfigurationNotFoundException;
use ncc\Exceptions\ComponentVersionNotFoundException;
use ncc\Exceptions\ConstantReadonlyException;
use ncc\Exceptions\DirectoryNotFoundException;
@ -12,6 +13,7 @@
use ncc\Exceptions\InvalidCredentialsEntryException;
use ncc\Exceptions\InvalidPackageException;
use ncc\Exceptions\InvalidPackageNameException;
use ncc\Exceptions\InvalidProjectBuildConfiguration;
use ncc\Exceptions\InvalidProjectConfigurationException;
use ncc\Exceptions\InvalidProjectNameException;
use ncc\Exceptions\InvalidScopeException;
@ -128,6 +130,21 @@
*/
const InvalidConstantNameException = -1719;
/**
* @see PackagePreparationFailedException
*/
const PackagePreparationFailedException = -1720;
/**
* @see BuildConfigurationNotFoundException
*/
const BuildConfigurationNotFoundException = -1721;
/**
* @see InvalidProjectBuildConfiguration
*/
const InvalidProjectBuildConfiguration = -1722;
/**
* All the exception codes from NCC
*/
@ -151,6 +168,9 @@
self::UnsupportedPackageException,
self::NotImplementedException,
self::InvalidPackageException,
self::InvalidConstantNameException
self::InvalidConstantNameException,
self::PackagePreparationFailedException,
self::BuildConfigurationNotFoundException,
self::InvalidProjectBuildConfiguration
];
}

View file

@ -0,0 +1,8 @@
<?php
namespace ncc\Abstracts\Options;
abstract class BuildConfigurationValues
{
const DefaultConfiguration = 'default';
}

View file

@ -3,6 +3,7 @@
namespace ncc\Classes\PhpExtension;
use ArrayIterator;
use ncc\Abstracts\ComponentFileExtensions;
use ncc\Exceptions\AutoloadGeneratorException;
use ncc\Exceptions\NoUnitsFoundException;
use ncc\Objects\ProjectConfiguration;
@ -45,9 +46,11 @@
{
// Construct configuration
$configuration = new Config([$src]);
$configuration->setFollowSymlinks(false);
$configuration->setFollowSymlinks(false); // Don't follow symlinks, it won't work on some systems.
$configuration->setOutputFile($output);
$configuration->setTrusting(false); // Paranoid
// Official PHP file extensions that are missing from the default configuration (whatever)
$configuration->setInclude(ComponentFileExtensions::Php);
// Construct factory
$factory = new Factory();

View file

@ -1,21 +1,36 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace ncc\Classes\PhpExtension;
use FilesystemIterator;
use ncc\Abstracts\CompilerOptions;
use ncc\Abstracts\ComponentFileExtensions;
use ncc\Abstracts\Options\BuildConfigurationValues;
use ncc\Abstracts\Versions;
use ncc\Exceptions\BuildConfigurationNotFoundException;
use ncc\Exceptions\PackagePreparationFailedException;
use ncc\Interfaces\CompilerInterface;
use ncc\ncc;
use ncc\Objects\Package;
use ncc\Objects\ProjectConfiguration;
use ncc\ThirdParty\theseer\DirectoryScanner\DirectoryScanner;
use ncc\ThirdParty\theseer\DirectoryScanner\Exception;
use ncc\Utilities\Console;
use SplFileInfo;
class Compiler implements CompilerInterface
{
/**
* @var ProjectConfiguration
*/
private ProjectConfiguration $project;
private $project;
/**
* @var Package|null
*/
private $package;
/**
* @param ProjectConfiguration $project
@ -25,8 +40,43 @@
$this->project = $project;
}
public function prepare(array $options)
/**
* 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 array $options
* @param string $src
* @param string $build_configuration
* @return void
* @throws PackagePreparationFailedException
*/
public function prepare(array $options, string $src, string $build_configuration=BuildConfigurationValues::DefaultConfiguration)
{
// Auto-select the default build configuration
if($build_configuration == BuildConfigurationValues::DefaultConfiguration)
{
$build_configuration = $this->project->Build->DefaultConfiguration;
}
// Select the build configuration
try
{
$selected_build_configuration = $this->project->Build->getBuildConfiguration($build_configuration);
}
catch (BuildConfigurationNotFoundException $e)
{
throw new PackagePreparationFailedException($e->getMessage(), $e);
}
// Create the package object
$this->package = new Package();
$this->package->Assembly = $this->project->Assembly;
$this->package->Dependencies = $this->project->Build->Dependencies;
$this->package->Header->RuntimeConstants = $selected_build_configuration->DefineConstants;
$this->package->Header->CompilerExtension = $this->project->Project->Compiler;
$this->package->Header->CompilerVersion = NCC_VERSION_NUMBER;
if(ncc::cliMode())
{
Console::out('Building autoloader');
@ -36,10 +86,83 @@
// First scan the project files and create a file struct.
$DirectoryScanner = new DirectoryScanner();
$DirectoryScanner->unsetFlag(FilesystemIterator::FOLLOW_SYMLINKS);
try
{
$DirectoryScanner->unsetFlag(FilesystemIterator::
FOLLOW_SYMLINKS);
}
catch (Exception $e)
{
throw new PackagePreparationFailedException('Cannot unset flag \'FOLLOW_SYMLINKS\' in DirectoryScanner, ' . $e->getMessage(), $e);
}
// Include file components that can be compiled
$DirectoryScanner->setIncludes(ComponentFileExtensions::Php);
$DirectoryScanner->setExcludes($selected_build_configuration->ExcludeFiles);
// Scan for components first.
Console::out('Scanning for components...', false);
/** @var SplFileInfo $item */
foreach($DirectoryScanner($src, true) as $item)
{
// Ignore directories, they're not important. :-)
if(is_dir($item->getPath()))
continue;
$Component = new Package\Component();
$Component->Name = $item->getPath();
$this->package->Components[] = $Component;
var_dump($item->getPath());
var_dump($item);
}
if(count($this->package->Components) > 0)
{
Console::out(count($this->package->Components) . ' component(s) found');
}
else
{
Console::out('No components found');
}
// Now scan for resources
Console::out('Scanning for resources...', false);
$DirectoryScanner->setExcludes(array_merge(
$selected_build_configuration->ExcludeFiles, ComponentFileExtensions::Php
));
// Scan for components first.
/** @var SplFileInfo $item */
foreach($DirectoryScanner($src, true) as $item)
{
// Ignore directories, they're not important. :-)
if(is_dir($item->getPath()))
continue;
$Resource = new Package\Resource();
$Resource->Name = $item->getPath();
$this->package->Resources[] = $Resource;
var_dump($item->getPath());
var_dump($item);
}
if(count($this->package->Resources) > 0)
{
Console::out(count($this->package->Resources) . ' resources(s) found');
}
else
{
Console::out('No resources found');
}
var_dump($this->package);
}
public function build(array $options)
public function build(array $options, string $src)
{
// TODO: Implement build() method.
}

View file

@ -0,0 +1,25 @@
<?php
/** @noinspection PhpPropertyOnlyWrittenInspection */
namespace ncc\Exceptions;
use Exception;
use ncc\Abstracts\ExceptionCodes;
use Throwable;
class BuildConfigurationNotFoundException extends Exception
{
private ?Throwable $previous;
/**
* @param string $message
* @param Throwable|null $previous
*/
public function __construct(string $message = "", ?Throwable $previous = null)
{
parent::__construct($message, ExceptionCodes::BuildConfigurationNotFoundException, $previous);
$this->message = $message;
$this->previous = $previous;
}
}

View file

@ -0,0 +1,25 @@
<?php
/** @noinspection PhpPropertyOnlyWrittenInspection */
namespace ncc\Exceptions;
use Exception;
use ncc\Abstracts\ExceptionCodes;
use Throwable;
class InvalidProjectBuildConfiguration extends Exception
{
private ?Throwable $previous;
/**
* @param string $message
* @param Throwable|null $previous
*/
public function __construct(string $message = "", ?Throwable $previous = null)
{
parent::__construct($message, ExceptionCodes::InvalidProjectBuildConfiguration, $previous);
$this->message = $message;
$this->previous = $previous;
}
}

View file

@ -0,0 +1,28 @@
<?php
/** @noinspection PhpPropertyOnlyWrittenInspection */
namespace ncc\Exceptions;
use Exception;
use ncc\Abstracts\ExceptionCodes;
use Throwable;
class PackagePreparationFailedException extends Exception
{
/**
* @var Throwable|null
*/
private ?Throwable $previous;
/**
* @param string $message
* @param Throwable|null $previous
*/
public function __construct(string $message = "", ?Throwable $previous = null)
{
parent::__construct($message, ExceptionCodes::PackagePreparationFailedException, $previous);
$this->message = $message;
$this->previous = $previous;
}
}

View file

@ -4,7 +4,7 @@
interface CompilerInterface
{
public function prepare(array $options);
public function prepare(array $options, string $src);
public function build(array $options);
public function build(array $options, string $src);
}

View file

@ -54,7 +54,7 @@
* @return bool
* @throws InvalidProjectConfigurationException
*/
public function validate(bool $throw_exception=false): bool
public function validate(bool $throw_exception=True): bool
{
if(!$this->Assembly->validate($throw_exception))
return false;

View file

@ -83,7 +83,7 @@
* @return bool
* @throws InvalidProjectConfigurationException
*/
public function validate(bool $throw_exception=false): bool
public function validate(bool $throw_exception=True): bool
{
if(!preg_match(RegexPatterns::UUIDv4, $this->UUID))
{

View file

@ -4,6 +4,8 @@
namespace ncc\Objects\ProjectConfiguration;
use ncc\Exceptions\BuildConfigurationNotFoundException;
use ncc\Exceptions\InvalidProjectBuildConfiguration;
use ncc\Utilities\Functions;
/**
@ -80,6 +82,71 @@
$this->Configurations = [];
}
/**
* Validates the build configuration object
*
* @param bool $throw_exception
* @return bool
* @throws InvalidProjectBuildConfiguration
*/
public function validate(bool $throw_exception=True): bool
{
// TODO: Implement further validation logic
// Check for duplicate configuration names
$build_configurations = [];
foreach($this->Configurations as $configuration)
{
if(in_array($configuration->Name, $build_configurations))
{
if($throw_exception)
throw new InvalidProjectBuildConfiguration('The build configuration \'' . $configuration->Name . '\' is already defined, build configuration names must be unique');
return false;
}
}
return true;
}
/**
* Returns an array of all the build configurations defined in the project configuration
*
* @return array
*/
public function getBuildConfigurations(): array
{
$build_configurations = [];
foreach($this->Configurations as $configuration)
{
$build_configurations[] = $configuration->Name;
}
return $build_configurations;
}
/**
* Returns the build configurations defined in the project configuration, throw an
* exception if there is no such configuration defined in the project configuration
*
* @param string $name
* @return BuildConfiguration
* @throws BuildConfigurationNotFoundException
*/
public function getBuildConfiguration(string $name): BuildConfiguration
{
foreach($this->Configurations as $configuration)
{
if($configuration->Name == $name)
{
return $configuration;
}
}
throw new BuildConfigurationNotFoundException('The build configuration ' . $name . ' does not exist');
}
/**
* Returns an array representation of the object
*

View file

@ -0,0 +1,8 @@
# Example Project
This is a simple library that provides very basic functionality,
this project is a means to test the compiler capabilities of NCC
### Generating a project configuration file.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace ExampleLibrary;
use ExampleLibrary\Exceptions\FileNotFoundException;
use ExampleLibrary\Objects\Person;
class ExampleLibrary
{
/**
* @var string[]
*/
private $FirstNames;
/**
* @var string[]
*/
private $LastNames;
/**
* Public Constructor
*
* @throws FileNotFoundException
*/
public function __construct()
{
if(!file_exists(__DIR__ . DIRECTORY_SEPARATOR . 'Data' . DIRECTORY_SEPARATOR . 'first_names.txt'))
throw new FileNotFoundException('The file first_names.txt does not exist in the data directory.');
if(!file_exists(__DIR__ . DIRECTORY_SEPARATOR . 'Data' . DIRECTORY_SEPARATOR . 'last_names.txt'))
throw new FileNotFoundException('The file last_names.txt does not exist in the data directory.');
$first_names = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'Data' . DIRECTORY_SEPARATOR . 'first_names.txt');
$this->FirstNames = explode("\n", $first_names);
$last_names = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'Data' . DIRECTORY_SEPARATOR . 'last_names.txt');
$this->LastNames = explode("\n", $last_names);
}
/**
* Returns an array of randomly generated
*
* @param int $amount
* @return array
* @throws Exceptions\InvalidNameException
*/
public function generatePeople(int $amount=10): array
{
$results = [];
for ($k = 0 ; $k < $amount; $k++)
{
$FullName = implode(' ', [
$this->FirstNames[array_rand($this->FirstNames)],
$this->LastNames[array_rand($this->LastNames)]
]);
$results[] = new Person($FullName);
}
return $results;
}
}

View file

@ -0,0 +1,29 @@
<?php
/** @noinspection PhpPropertyOnlyWrittenInspection */
namespace ExampleLibrary\Exceptions;
use Exception;
use Throwable;
class FileNotFoundException extends Exception
{
/**
* @var Throwable|null
*/
private ?Throwable $previous;
/**
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->message = $message;
$this->code = $code;
$this->previous = $previous;
}
}

View file

@ -0,0 +1,26 @@
<?php
/** @noinspection PhpPropertyOnlyWrittenInspection */
namespace ExampleLibrary\Exceptions;
use Exception;
use Throwable;
class InvalidNameException extends Exception
{
private ?Throwable $previous;
/**
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->message = $message;
$this->code = $code;
$this->previous = $previous;
}
}

View file

@ -0,0 +1,123 @@
<?php
namespace ExampleLibrary\Objects;
use ExampleLibrary\Exceptions\InvalidNameException;
class Person
{
/**
* @var string
*/
private string $FirstName;
/**
* @var string
*/
private string $LastName;
/**
* Public Constructor
*
* @param string|null $name
* @throws InvalidNameException
*/
public function __construct(?string $name=null)
{
if($name !== null)
{
$exploded_name = explode(' ', $name);
if(count($exploded_name) < 2)
{
throw new InvalidNameException('The given name must contain a first and last name.');
}
$this->FirstName = $exploded_name[0];
$this->LastName = $exploded_name[1];
}
}
/**
* Sets the first name of the person.
*
* @param string $FirstName
*/
public function setFirstName(string $FirstName): void
{
$this->FirstName = $FirstName;
}
/**
* Gets the last name of the person.
*
* @return string
*/
public function getLastName(): string
{
return $this->LastName;
}
/**
* Sets the last name of the person.
*
* @param string $LastName
*/
public function setLastName(string $LastName): void
{
$this->LastName = $LastName;
}
/**
* Gets the first name of the person.
*
* @return string
*/
public function getFirstName(): string
{
return $this->FirstName;
}
/**
* Returns a string representation of the person.
*
* @return string
*/
public function __toString()
{
return implode(' ', [$this->FirstName, $this->LastName]);
}
/**
* Returns an array representation of the person
*
* @return array
*/
public function toArray(): array
{
return [
'first_name' => $this->FirstName,
'last_name' => $this->LastName
];
}
/**
* Constructs object from an array representation
*
* @param array $data
* @return Person
*/
public static function fromArray(array $data): Person
{
$person = new Person();
if(isset($data['first_name']))
$person->FirstName = $data['first_name'];
if(isset($data['last_name']))
$person->LastName = $data['last_name'];
return $person;
}
}