- Fixed issue when registering ncc's extension, when using the INSTALLER, the installation path used in the process

appears to be incorrect, added a optional parameter to the `registerExtension` method to allow the installer to pass
   the correct installation path.
 - Implemented support in the AST traversal for the PHP statements `include`, `include_once`, `require`, and
   `require_once`. These statements are transformed into function calls. With this change, ncc can correctly handle and
   import files from system packages or direct binary package files.
This commit is contained in:
Netkas 2023-10-17 17:01:40 -04:00
parent c736a896fb
commit 173032df72
No known key found for this signature in database
GPG key ID: 5DAF58535614062B
12 changed files with 557 additions and 74 deletions

View file

@ -7,9 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2.0.3] - Unreleased
### Added
- Implemented support in the AST traversal for the PHP statements `include`, `include_once`, `require`, and
`require_once`. These statements are transformed into function calls. With this change, ncc can correctly handle and
import files from system packages or direct binary package files.
### Fixed
- When finding package versions in the package lock, ncc will try to find a satisfying version rather than the exact
version, this is to prevent errors when the package lock contains a version that is not available in the repository.
- Fixed issue when registering ncc's extension, when using the INSTALLER, the installation path used in the process
appears to be incorrect, added a optional parameter to the `registerExtension` method to allow the installer to pass
the correct installation path.
## [2.0.2] - 2023-10-13

View file

@ -242,23 +242,6 @@
$NCC_FILESYSTEM->mkdir($NCC_INSTALL_PATH, 0755);
try
{
if(is_file(__DIR__ . DIRECTORY_SEPARATOR . 'default_repositories.json'))
{
Functions::initializeFiles(Functions::loadJsonFile(__DIR__ . DIRECTORY_SEPARATOR . 'default_repositories.json', Functions::FORCE_ARRAY));
}
else
{
Functions::initializeFiles();
}
}
catch(Exception $e)
{
Console::outException('Cannot initialize NCC files, ' . $e->getMessage(), $e, 1);
return;
}
// Copy files to the installation path
try
{
@ -302,6 +285,24 @@
Console::inlineProgressBar($processed_items, $total_items);
}
// Initialize ncc's files
try
{
if(is_file(__DIR__ . DIRECTORY_SEPARATOR . 'default_repositories.json'))
{
Functions::initializeFiles($NCC_INSTALL_PATH, Functions::loadJsonFile(__DIR__ . DIRECTORY_SEPARATOR . 'default_repositories.json', Functions::FORCE_ARRAY));
}
else
{
Functions::initializeFiles($NCC_INSTALL_PATH);
}
}
catch(Exception $e)
{
Console::outException('Cannot initialize NCC files, ' . $e->getMessage(), $e, 1);
return;
}
// Generate executable shortcut
try

View file

@ -58,7 +58,7 @@
try
{
Functions::initializeFiles($default_repositories);
Functions::initializeFiles(null, $default_repositories);
}
catch(Exception $e)
{

View file

@ -639,6 +639,25 @@
return Resource::fromArray(ZiProto::decode($this->getByPointer($pointer, $length)));
}
/**
* Searches the package's directory for a file that matches the given filename
*
* @param string $filename
* @return string|false
*/
public function find(string $filename): string|false
{
foreach($this->headers[PackageStructure::DIRECTORY] as $name => $location)
{
if(str_ends_with($name, $filename))
{
return $name;
}
}
return false;
}
/**
* Returns the offset of the package
*

View file

@ -24,6 +24,7 @@
use ncc\ThirdParty\nikic\PhpParser\Comment;
use ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\NodeTraverser;
use ReflectionClass;
use ReflectionException;
use RuntimeException;
@ -60,9 +61,10 @@
*/
public static function extractClasses(Node|array $node, string $prefix=''): array
{
$classes = [];
if(is_array($node))
{
$classes = [];
foreach($node as $sub_node)
{
/** @noinspection SlowArrayOperationsInLoopInspection */
@ -71,8 +73,6 @@
return $classes;
}
$classes = [];
if ($node instanceof Node\Stmt\ClassLike)
{
$classes[] = $prefix . $node->name;
@ -80,9 +80,9 @@
if ($node instanceof Node\Stmt\Namespace_)
{
if ($node->name && $node->name->parts)
if ($node->name && $node->name->getParts())
{
$prefix .= implode('\\', $node->name->parts) . '\\';
$prefix .= implode('\\', $node->name->getParts()) . '\\';
}
else
{
@ -254,4 +254,19 @@
throw new RuntimeException("Unknown node type \"$nodeType\"");
}
/**
* Transforms include, include_once, require and require_once statements into function calls.
*
* @param Node|array $stmts The AST node or array of nodes to transform.
* @param string|null $package Optionally. The package name to pass to the transformed function calls.
* @return Node|array The transformed AST node or array of nodes.
*/
public static function transformRequireCalls(Node|array $stmts, ?string $package=null): Node|array
{
$traverser = new NodeTraverser();
$traverser->addVisitor(new ExpressionTraverser($package));
return $traverser->traverse($stmts);
}
}

View file

@ -0,0 +1,74 @@
<?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\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\NodeVisitorAbstract;
use ncc\Utilities\Console;
class ExpressionTraverser extends NodeVisitorAbstract
{
/**
* @var string|null
*/
private ?string $package;
/**
* ExpressionTraverser constructor.
*
* @param string|null $package
*/
public function __construct(?string $package)
{
$this->package = $package;
}
/**
* @param Node $node
* @return array|int|Node|null
*/
public function leaveNode(Node $node): array|int|Node|null
{
if($node instanceof Node\Expr\Include_)
{
Console::outDebug(sprintf('Processing ExpressionTraverser on: %s', $node->getType()));
$args = [$node->expr];
if(!is_null($this->package))
{
$args[] = new Node\Arg(new Node\Scalar\String_($this->package));
}
$types = [
Node\Expr\Include_::TYPE_INCLUDE => '\ncc\Classes\Runtime::runtimeInclude',
Node\Expr\Include_::TYPE_INCLUDE_ONCE => '\ncc\Classes\Runtime::runtimeIncludeOnce',
Node\Expr\Include_::TYPE_REQUIRE => '\ncc\Classes\Runtime::runtimeRequire',
Node\Expr\Include_::TYPE_REQUIRE_ONCE => '\ncc\Classes\Runtime::runtimeRequireOnce',
];
return new Node\Expr\FuncCall(new Node\Name($types[$node->type]), $args);
}
return null;
}
}

View file

@ -53,6 +53,9 @@
try
{
$stmts = (new ParserFactory())->create(ParserFactory::PREFER_PHP7)->parse(IO::fread($file_path));
$stmts = AstWalker::transformRequireCalls(
$stmts, $this->getProjectManager()->getProjectConfiguration()->getAssembly()->getPackage()
);
$component = new Component($component_name, ZiProto::encode($stmts), ComponentDataType::AST);
$component->addFlag(ComponentFlags::PHP_AST);

View file

@ -29,6 +29,8 @@
use ncc\Enums\FileDescriptor;
use ncc\Enums\Flags\PackageFlags;
use ncc\Enums\Options\BuildConfigurationOptions;
use ncc\Enums\Options\ComponentDecodeOptions;
use ncc\Enums\PackageDirectory;
use ncc\Enums\Versions;
use ncc\Exceptions\ConfigurationException;
use ncc\Exceptions\ImportException;
@ -39,8 +41,12 @@
use ncc\Extensions\ZiProto\ZiProto;
use ncc\Managers\PackageManager;
use ncc\Objects\Package\Metadata;
use ncc\Utilities\Console;
use ncc\Utilities\IO;
use ncc\Utilities\Resolver;
use ncc\Utilities\Validate;
use RuntimeException;
use Throwable;
class Runtime
{
@ -59,6 +65,11 @@
*/
private static $package_manager;
/**
* @var array
*/
private static $included_files = [];
/**
* Executes the main execution point of an imported package and returns the evaluated result
* This method may exit the program without returning a value
@ -170,6 +181,7 @@
}
$entry = self::getPackageManager()->getPackageLock()->getEntry($package);
self::$imported_packages[$package] = $entry->getPath($version);
foreach($entry->getClassMap($version) as $class => $component_name)
{
@ -197,8 +209,6 @@
}
}
self::$imported_packages[$package] = $entry->getPath($version);
if(isset($entry->getMetadata($version)->getOptions()[PackageFlags::STATIC_DEPENDENCIES]))
{
// Fake import the dependencies
@ -345,7 +355,6 @@
if(is_string(self::$class_map[$class]) && is_file(self::$class_map[$class]))
{
require_once self::$class_map[$class];
return;
}
}
@ -361,4 +370,274 @@
return self::$package_manager;
}
/**
* Returns an array of included files both from the php runtime and ncc runtime
*
* @return array
*/
public static function runtimeGetIncludedFiles(): array
{
return array_merge(get_included_files(), self::$included_files);
}
/**
* Evaluates and executes PHP code with error handling, this function
* gracefully handles <?php ?> tags and exceptions the same way as the
* require/require_once/include/include_once expressions
*
* @param string $code The PHP code to be executed
*/
public static function extendedEvaluate(string $code): void
{
if(ob_get_level() > 0)
{
ob_clean();
}
$exceptions = [];
$code = preg_replace_callback('/<\?php(.*?)\?>/s', static function ($matches) use (&$exceptions)
{
ob_start();
try
{
eval($matches[1]);
}
catch (Throwable $e)
{
$exceptions[] = $e;
}
return ob_get_clean();
}, $code);
ob_start();
try
{
eval('?>' . $code);
}
catch (Throwable $e)
{
$exceptions[] = $e;
}
if (!empty($exceptions))
{
print(ob_get_clean());
$exception_stack = null;
foreach ($exceptions as $e)
{
if($exception_stack === null)
{
$exception_stack = $e;
}
else
{
$exception_stack = new Exception($exception_stack->getMessage(), $exception_stack->getCode(), $e);
}
}
throw new RuntimeException('An exception occurred while evaluating the code', 0, $exception_stack);
}
print(ob_get_clean());
}
/**
* Returns the content of the aquired file
*
* @param string $path
* @param string|null $package
* @return string
* @throws ConfigurationException
* @throws IOException
* @throws OperationException
* @throws PathNotFoundException
*/
private static function acquireFile(string $path, ?string $package=null): string
{
$cwd_checked = false; // sanity check to prevent checking the cwd twice
// Check if the file is absolute
if(is_file($path))
{
Console::outDebug(sprintf('Acquired file "%s" from absolute path', $path));
return IO::fread($path);
}
// Since $package is not null, let's try to acquire the file from the package
if($package !== null && isset(self::$imported_packages[$package]))
{
$base_path = basename($path);
if(self::$imported_packages[$package] instanceof PackageReader)
{
$acquired_file = self::$imported_packages[$package]->find($base_path);
Console::outDebug(sprintf('Acquired file "%s" from package "%s"', $path, $package));
return match (Resolver::componentType($acquired_file))
{
PackageDirectory::RESOURCES => self::$imported_packages[$package]->getResource(Resolver::componentName($acquired_file))->getData(),
PackageDirectory::COMPONENTS => self::$imported_packages[$package]->getComponent(Resolver::componentName($acquired_file))->getData([ComponentDecodeOptions::AS_FILE]),
default => throw new IOException(sprintf('Unable to acquire file "%s" from package "%s" because it is not a resource or component', $path, $package)),
};
}
if(is_dir(self::$imported_packages[$package]))
{
$base_path = basename($path);
foreach(IO::scan(self::$imported_packages[$package]) as $file)
{
if(str_ends_with($file, $base_path))
{
Console::outDebug(sprintf('Acquired file "%s" from package "%s"', $path, $package));
return IO::fread($file);
}
}
}
}
// If not, let's try the include_path
foreach(explode(PATH_SEPARATOR, get_include_path()) as $file_path)
{
if($file_path === '.' && !$cwd_checked)
{
$cwd_checked = true;
$file_path = getcwd();
}
if(is_file($file_path . DIRECTORY_SEPARATOR . $path))
{
Console::outDebug(sprintf('Acquired file "%s" from include_path', $path));
return IO::fread($file_path . DIRECTORY_SEPARATOR . $path);
}
if(is_file($file_path . DIRECTORY_SEPARATOR . basename($path)))
{
Console::outDebug(sprintf('Acquired file "%s" from include_path (using basename)', $path));
return IO::fread($file_path . DIRECTORY_SEPARATOR . basename($path));
}
}
// Check the current working directory
if(!$cwd_checked)
{
if(is_file(getcwd() . DIRECTORY_SEPARATOR . $path))
{
Console::outDebug(sprintf('Acquired file "%s" from current working directory', $path));
return IO::fread(getcwd() . DIRECTORY_SEPARATOR . $path);
}
if(is_file(getcwd() . DIRECTORY_SEPARATOR . basename($path)))
{
Console::outDebug(sprintf('Acquired file "%s" from current working directory (using basename)', $path));
return IO::fread(getcwd() . DIRECTORY_SEPARATOR . basename($path));
}
}
// Check the calling script's directory
$called_script_directory = dirname(debug_backtrace()[0]['file']);
$file_path = $called_script_directory . DIRECTORY_SEPARATOR . $path;
if(is_file($file_path))
{
Console::outDebug(sprintf('Acquired file "%s" from calling script\'s directory', $path));
return IO::fread($file_path);
}
throw new IOException(sprintf('Unable to acquire file "%s" because it does not exist', $path));
}
/**
* Includes a file at runtime
*
* @param string $path
* @param string|null $package
* @return void
*/
public static function runtimeInclude(string $path, ?string $package=null): void
{
try
{
$acquired_file = self::acquireFile($path, $package);
}
catch(Exception $e)
{
$package ?
Console::outWarning(sprintf('Failed to acquire file "%s" from package "%s" at runtime: %s', $path, $package, $e->getMessage())) :
Console::outWarning(sprintf('Failed to acquire file "%s" at runtime: %s', $path, $e->getMessage()));
return;
}
if(!in_array($path, self::$included_files, true))
{
self::$included_files[] = $path;
}
self::extendedEvaluate($acquired_file);
}
/**
* Includes a file at runtime if it's not already included
*
* @param string $path
* @param string|null $package
* @return void
*/
public static function runtimeIncludeOnce(string $path, ?string $package=null): void
{
if(in_array($path, self::runtimeGetIncludedFiles(), true))
{
return;
}
self::runtimeInclude($path, $package);
}
/**
* Requires a file at runtime, throws an exception if the file failed to require
*
* @param string $path
* @param string|null $package
* @return void
*/
public static function runtimeRequire(string $path, ?string $package=null): void
{
try
{
$acquired_file = self::acquireFile($path, $package);
}
catch(Exception $e)
{
$package ?
throw new RuntimeException(sprintf('Failed to acquire file "%s" from package "%s" at runtime: %s', $path, $package, $e->getMessage()), $e->getCode(), $e) :
throw new RuntimeException(sprintf('Failed to acquire file "%s" at runtime: %s', $path, $e->getMessage()), $e->getCode(), $e);
}
if(!in_array($path, self::$included_files, true))
{
self::$included_files[] = $path;
}
self::extendedEvaluate($acquired_file);
}
/**
* Requires a file at runtime if it's not already required
*
* @param string $path
* @return void
*/
public static function runtimeRequireOnce(string $path): void
{
if(in_array($path, self::runtimeGetIncludedFiles(), true))
{
return;
}
self::runtimeRequire($path);
}
}

View file

@ -285,13 +285,13 @@
}
/**
* Initializes ncc system files
* Initializes the necessary files and directories for the ncc application
*
* @param array $default_repositories
* @return void
* @param string|null $install_path The installation path of ncc (optional)
* @param array $default_repositories The default repositories to initialize (optional)
* @throws OperationException
*/
public static function initializeFiles(array $default_repositories=[]): void
public static function initializeFiles(?string $install_path=null, array $default_repositories=[]): void
{
if(Resolver::resolveScope() !== Scopes::SYSTEM)
{
@ -342,7 +342,7 @@
try
{
self::registerExtension($filesystem);
self::registerExtension($filesystem, $install_path);
}
catch(Exception $e)
{
@ -351,23 +351,30 @@
}
/**
* Register the ncc extension with the given filesystem.
* Registers the ncc extension with the given filesystem and optional install path.
*
* @param Filesystem $filesystem The filesystem object used for file operations.
* @throws IOException If the extension cannot be registered.
* @throws NotSupportedException If `get_include_path()` function is not available.
* @throws PathNotFoundException If the default include path is not available.
* @param Filesystem $filesystem The filesystem to register the extension with
* @param string|null $install_path The optional install path for the extension
* @return void
* @throws IOException
* @throws NotSupportedException
* @throws PathNotFoundException
*/
private static function registerExtension(Filesystem $filesystem): void
private static function registerExtension(Filesystem $filesystem, ?string $install_path=null): void
{
if(!function_exists('get_include_path'))
{
throw new NotSupportedException('Cannot register ncc extension, get_include_path() is not available');
}
if($install_path === null)
{
$install_path = NCC_EXEC_LOCATION;
}
$default_share = DIRECTORY_SEPARATOR . 'usr' . DIRECTORY_SEPARATOR . 'share' . DIRECTORY_SEPARATOR . 'php';
$include_paths = explode(':', get_include_path());
$extension = str_ireplace('%ncc_install', NCC_EXEC_LOCATION, IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'extension'));
$extension = str_ireplace('%ncc_install', $install_path, IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'extension'));
if(in_array($default_share, $include_paths))
{

View file

@ -22,8 +22,10 @@
namespace ncc\Utilities;
use InvalidArgumentException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\PathNotFoundException;
use RuntimeException;
use SplFileInfo;
use SplFileObject;
@ -122,4 +124,48 @@
Console::outDebug(sprintf('reading %s', $uri));
return $file->fread($length);
}
/**
* Returns an array of all files in the specified path
*
* @param string $path
* @param bool $recursive
* @return array
* @throws IOException
*/
public static function scan(string $path, bool $recursive = true): array
{
if (!is_readable($path))
{
throw new IOException(sprintf('Directory does not exist or is not readable: %s', $path));
}
$files = [];
$items = scandir($path, SCANDIR_SORT_NONE);
if ($items === false)
{
throw new IOException(sprintf('Unable to list directory items: %s', $path));
}
foreach ($items as $file)
{
if ($file === '.' || $file === '..')
{
continue;
}
$file_path = $path . DIRECTORY_SEPARATOR . $file;
if (is_dir($file_path) && $recursive)
{
/** @noinspection SlowArrayOperationsInLoopInspection */
$files = array_merge($files, self::scan($file_path));
continue;
}
$files[] = $file_path;
}
return $files;
}
}

View file

@ -24,7 +24,9 @@
namespace ncc\Utilities;
use InvalidArgumentException;
use ncc\Enums\LogLevel;
use ncc\Enums\PackageDirectory;
use ncc\Enums\Scopes;
use ncc\Enums\Types\ProjectType;
use ncc\Exceptions\NotSupportedException;
@ -256,4 +258,69 @@
return false;
}
/**
* Get the component type based on the file name
*
* @param string $component_path Component name
* @return int Component type
* @throws InvalidArgumentException If the component name is invalid
* @see PackageDirectory
*/
public static function componentType(string $component_path): int
{
// Check for empty string and presence of ":"
if (empty($component_path) || !str_contains($component_path, ':'))
{
throw new InvalidArgumentException(sprintf('Invalid component format "%s"', $component_path));
}
// Get the prefix before ":" and remove "@" character
$file_stub_code = str_ireplace('@', '', explode(':', $component_path, 2)[0]);
// Check if the prefix is numeric
if (!is_numeric($file_stub_code))
{
throw new InvalidArgumentException(sprintf('Invalid component prefix "%s"', $file_stub_code));
}
return match ((int)$file_stub_code)
{
PackageDirectory::METADATA => PackageDirectory::METADATA,
PackageDirectory::ASSEMBLY => PackageDirectory::ASSEMBLY,
PackageDirectory::EXECUTION_UNITS => PackageDirectory::EXECUTION_UNITS,
PackageDirectory::INSTALLER => PackageDirectory::INSTALLER,
PackageDirectory::DEPENDENCIES => PackageDirectory::DEPENDENCIES,
PackageDirectory::CLASS_POINTER => PackageDirectory::CLASS_POINTER,
PackageDirectory::RESOURCES => PackageDirectory::RESOURCES,
PackageDirectory::COMPONENTS => PackageDirectory::COMPONENTS,
default => throw new InvalidArgumentException(sprintf('Invalid component type "%s"', $component_path)),
};
}
/**
* Returns the component name based on the file name
*
* @param string $component_path
* @return string
*/
public static function componentName(string $component_path): string
{
// Check for empty string and presence of ":"
if (empty($component_path) || !str_contains($component_path, ':'))
{
throw new InvalidArgumentException(sprintf('Invalid component format "%s"', $component_path));
}
// Get the prefix before ":" and remove "@" character
$file_stub_code = str_ireplace('@', '', explode(':', $component_path, 2)[0]);
// Check if the prefix is numeric
if (!is_numeric($file_stub_code))
{
throw new InvalidArgumentException(sprintf('Invalid component prefix "%s"', $file_stub_code));
}
return explode(':', $component_path, 2)[1];
}
}

View file

@ -1,36 +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.
*
*/
$build_directory = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'build';
$autoload_path = $build_directory . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'autoload.php';
if(!is_dir($build_directory))
{
throw new \RuntimeException('Build directory does not exist, to run tests you must build the project.');
}
if(!is_file($autoload_path))
{
throw new \RuntimeException('Autoload file does not exist in \'' . $build_directory .'\', to run tests you must build the project.');
}
require($autoload_path);