/**
* Generates the C sources from Zephir without compiling them
*
* @param CommandInterface $command
* @return bool
* @throws Exception
*/
public function generate(CommandInterface $command)
{
/**
* Get global namespace
*/
$namespace = $this->checkDirectory();
/**
* Check whether there are external dependencies
*/
$externalDependencies = $this->config->get('external-dependencies');
if (is_array($externalDependencies)) {
foreach ($externalDependencies as $dependencyNs => $location) {
if (!file_exists($location)) {
throw new CompilerException('Location of dependency "' . $dependencyNs . '" does not exist. Check the config.json for more information');
}
$this->addExternalDependency($dependencyNs, $location);
}
}
/**
* Check if there are module/request/global destructors
*/
$destructors = $this->config->get('destructors');
if (is_array($destructors)) {
$invokeDestructors = $this->processCodeInjection($destructors);
$includes = $invokeDestructors[0];
$destructors = $invokeDestructors[1];
}
/**
* Check if there are module/request/global initializers
*/
$initializers = $this->config->get('initializers');
if (is_array($initializers)) {
$invokeInitializers = $this->processCodeInjection($initializers);
$includes = $invokeInitializers[0];
$initializers = $invokeInitializers[1];
}
/**
* Round 1. pre-compile all files in memory
*/
$this->recursivePreCompile(str_replace('\\', DIRECTORY_SEPARATOR, $namespace));
if (!count($this->files)) {
throw new Exception("Zephir files to compile couldn't be found. Did you add a first class to the extension?");
}
/**
* Round 2. Check 'extends' and 'implements' dependencies
*/
foreach ($this->files as $compileFile) {
$compileFile->checkDependencies($this);
}
/**
* Sort the files by dependency ranking
*/
$files = array();
$rankedFiles = array();
$this->calculateDependencies($this->files);
foreach ($this->files as $rankFile) {
$rank = $rankFile->getClassDefinition()->getDependencyRank();
$rankedFiles[$rank][] = $rankFile;
}
krsort($rankedFiles);
foreach ($rankedFiles as $rank => $rankFiles) {
$files = array_merge($files, $rankFiles);
}
$this->files = $files;
/**
* Convert C-constants into PHP constants
*/
$constantsSources = $this->config->get('constants-sources');
if (is_array($constantsSources)) {
$this->loadConstantsSources($constantsSources);
}
/**
* Set extension globals
*/
$globals = $this->config->get('globals');
if (is_array($globals)) {
$this->setExtensionGlobals($globals);
}
/**
* Load function optimizers
*/
if (self::$loadedPrototypes === false) {
FunctionCall::addOptimizerDir(ZEPHIRPATH . 'Library/Optimizers/FunctionCall');
$optimizerDirs = $this->config->get('optimizer-dirs');
if (is_array($optimizerDirs)) {
foreach ($optimizerDirs as $directory) {
FunctionCall::addOptimizerDir(realpath($directory));
}
}
if (is_dir(ZEPHIRPATH . 'prototypes') && is_readable(ZEPHIRPATH . 'prototypes')) {
/**
* Load additional extension prototypes
* @var $file \DirectoryIterator
*/
foreach (new \DirectoryIterator(ZEPHIRPATH . 'prototypes') as $file) {
if (!$file->isDir()) {
$extension = str_replace('.php', '', $file);
if (!extension_loaded($extension)) {
require $file->getRealPath();
}
}
}
}
self::$loadedPrototypes = true;
}
/**
* Round 3. Compile all files to C sources
*/
$files = array();
$hash = "";
foreach ($this->files as $compileFile) {
/**
* Only compile classes in the local extension, ignore external classes
*/
if (!$compileFile->isExternal()) {
$compileFile->compile($this, $this->stringManager);
$compiledFile = $compileFile->getCompiledFile();
$methods = array();
$classDefinition = $compileFile->getClassDefinition();
foreach ($classDefinition->getMethods() as $method) {
$methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']';
if ($method->isInitializer() && $method->isStatic()) {
$this->internalInitializers[] = "\t" . $method->getName() . '(TSRMLS_C);';
}
}
$files[] = $compiledFile;
$hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']';
}
}
/**
* Round 3.2. Compile anonymous classes
*/
foreach ($this->anonymousFiles as $compileFile) {
$compileFile->compile($this, $this->stringManager);
$compiledFile = $compileFile->getCompiledFile();
$methods = array();
$classDefinition = $compileFile->getClassDefinition();
foreach ($classDefinition->getMethods() as $method) {
$methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']';
}
$files[] = $compiledFile;
$hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']';
}
$hash = md5($hash);
$this->compiledFiles = $files;
/**
* Round 3.3. Load extra C-sources
*/
$extraSources = $this->config->get('extra-sources');
if (is_array($extraSources)) {
$this->extraFiles = $extraSources;
} else {
$this->extraFiles = array();
}
/**
* Round 3.4. Load extra classes sources
*/
$extraClasses = $this->config->get('extra-classes');
if (is_array($extraClasses)) {
foreach ($extraClasses as $value) {
if (isset($value['source'])) {
$this->extraFiles[] = $value['source'];
}
}
}
/**
* Round 4. Create config.m4 and config.w32 files / Create project.c and project.h files
*/
$namespace = str_replace('\\', '_', $namespace);
$extensionName = $this->config->get('extension-name');
if (empty($extensionName) || !is_string($extensionName)) {
$extensionName = $namespace;
}
$needConfigure = $this->createConfigFiles($extensionName);
$needConfigure |= $this->createProjectFiles($extensionName);
$needConfigure |= $this->checkIfPhpized();
/**
* When a new file is added or removed we need to run configure again
*/
if (!$command instanceof CommandGenerate) {
if (!$this->fileSystem->exists(self::VERSION . '/compiled-files-sum')) {
$needConfigure = true;
$this->fileSystem->write(self::VERSION . '/compiled-files-sum', $hash);
} else {
if ($this->fileSystem->read(self::VERSION . '/compiled-files-sum') != $hash) {
$needConfigure = true;
$this->fileSystem->write(self::VERSION . '/compiled-files-sum', $hash);
}
}
}
/**
* Round 5. Generate concatenation functions
*/
$this->stringManager->genConcatCode();
$this->fcallManager->genFcallCode();
if ($this->config->get('stubs-run-after-generate', 'stubs')) {
$this->stubs($command, true);
}
return $needConfigure;
}