From 3530751a7c22549709068a84df5d60c6c44eb834 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Tue, 14 Jan 2014 19:52:16 -0800 Subject: [PATCH] GenCode - Add an optional caching mode. If CIVICRM_GENCODE_DIGEST is set, then GenCode will write out a digest of all its inputs & templates. If someone tries to rerun with the same inputs, then GenCode will do nothing. --- CRM/Core/CodeGen/Main.php | 87 ++++++++++++++++++++++++++++++---- CRM/Core/CodeGen/Util/File.php | 46 ++++++++++++++++++ xml/GenCode.php | 3 +- 3 files changed, 126 insertions(+), 10 deletions(-) diff --git a/CRM/Core/CodeGen/Main.php b/CRM/Core/CodeGen/Main.php index b63db4892c..f115ba7a27 100644 --- a/CRM/Core/CodeGen/Main.php +++ b/CRM/Core/CodeGen/Main.php @@ -11,11 +11,24 @@ class CRM_Core_CodeGen_Main { var $tplCodePath; var $schemaPath; // ex: schema/Schema.xml - function __construct($CoreDAOCodePath, $sqlCodePath, $phpCodePath, $tplCodePath, $smartyPluginDirs, $argCms, $argVersion, $schemaPath) { + /** + * @var string|NULL path in which to store a marker that indicates the last execution of + * GenCode. If a matching marker already exists, GenCode doesn't run. + */ + var $digestPath; + + /** + * @var string|NULL a digest of the inputs to the code-generator (eg the properties and source files) + */ + var $digest; + + function __construct($CoreDAOCodePath, $sqlCodePath, $phpCodePath, $tplCodePath, $smartyPluginDirs, $argCms, $argVersion, $schemaPath, $digestPath) { $this->CoreDAOCodePath = $CoreDAOCodePath; $this->sqlCodePath = $sqlCodePath; $this->phpCodePath = $phpCodePath; $this->tplCodePath = $tplCodePath; + $this->digestPath = $digestPath; + $this->digest = NULL; // default cms is 'drupal', if not specified $this->cms = isset($argCms) ? strtolower($argCms) : 'drupal'; @@ -39,6 +52,16 @@ class CRM_Core_CodeGen_Main { * */ function main() { + if (!empty($this->digestPath) && file_exists($this->digestPath)) { + if ($this->getDigest() === file_get_contents($this->digestPath)) { + echo "GenCode has previously executed. To force execution, please (a) omit CIVICRM_GENCODE_DIGEST\n"; + echo "or (b) remove {$this->digestPath} or (c) call GenCode with new parameters.\n"; + exit(); + } + // Once we start GenCode, the old build is invalid + unlink($this->digestPath); + } + echo "\ncivicrm_domain.version := ". $this->db_version . "\n\n"; if ($this->buildVersion < 1.1) { echo "The Database is not compatible for this version"; @@ -62,18 +85,15 @@ Alternatively you can get a version of CiviCRM that matches your PHP version $this->tables = $specification->tables; $this->runAllTasks(); + + if (!empty($this->digestPath)) { + file_put_contents($this->digestPath, $this->getDigest()); + } } function runAllTasks() { // TODO: This configuration can be manipulated dynamically. - $components = array( - 'CRM_Core_CodeGen_Config', - 'CRM_Core_CodeGen_Reflection', - 'CRM_Core_CodeGen_Schema', - 'CRM_Core_CodeGen_DAO', - 'CRM_Core_CodeGen_Test', - 'CRM_Core_CodeGen_I18n', - ); + $components = $this->getTasks(); foreach ($components as $component) { $task = new $component($this); @@ -86,4 +106,53 @@ Alternatively you can get a version of CiviCRM that matches your PHP version } } } + + /** + * @return array of class names; each class implements CRM_Core_CodeGen_ITask + */ + public function getTasks() { + $components = array( + 'CRM_Core_CodeGen_Config', + 'CRM_Core_CodeGen_Reflection', + 'CRM_Core_CodeGen_Schema', + 'CRM_Core_CodeGen_DAO', + 'CRM_Core_CodeGen_Test', + 'CRM_Core_CodeGen_I18n', + ); + return $components; + } + + /** + * Compute a digest based on the inputs to the code-generator (ie the properties + * of the codegen and the source files loaded by the codegen). + * + * @return string + */ + function getDigest() { + if ($this->digest === NULL) { + $srcDir = CRM_Core_CodeGen_Util_File::findCoreSourceDir(); + $files = CRM_Core_CodeGen_Util_File::findManyFiles(array( + array("$srcDir/CRM/Core/CodeGen", '*.php'), + array("$srcDir/xml", "*.php"), + array("$srcDir/xml", "*.tpl"), + array("$srcDir/xml", "*.xml"), + )); + + $properties = var_export(array( + CRM_Core_CodeGen_Util_File::digestAll($files), + $this->buildVersion, + $this->db_version, + $this->cms, + $this->CoreDAOCodePath, + $this->sqlCodePath, + $this->phpCodePath, + $this->tplCodePath, + $this->schemaPath, + $this->getTasks(), + ), TRUE); + + $this->digest = md5($properties); + } + return $this->digest; + } } diff --git a/CRM/Core/CodeGen/Util/File.php b/CRM/Core/CodeGen/Util/File.php index 43446b086c..630e07026e 100644 --- a/CRM/Core/CodeGen/Util/File.php +++ b/CRM/Core/CodeGen/Util/File.php @@ -31,4 +31,50 @@ class CRM_Core_CodeGen_Util_File { return $newTempDir; } + + /** + * Calculate a cumulative digest based on a collection of files + * + * @param array $files list of file names (strings) + * @param callable $digest a one-way hash function (string => string) + * @return string + */ + static function digestAll($files, $digest = 'md5') { + $buffer = ''; + foreach ($files as $file) { + $buffer .= $digest(file_get_contents($file)); + } + return $digest($buffer); + } + + /** + * Find the paths to all key files involved in the gen-code process + * + * @return array + * @throws RuntimeException + */ + static function findCoreSourceDir() { + $path = str_replace(DIRECTORY_SEPARATOR, '/', __DIR__); + if (!preg_match(':(.*)/CRM/Core/CodeGen/Util:', $path, $matches)) { + throw new RuntimeException("Failed to determine path of code-gen"); + } + + return $matches[1]; + } + + /** + * Find files in several directories using several filename patterns + * + * + * @param array $pairs each item is an array(0 => $searchBaseDir, 1 => $filePattern) + * @return array of file paths + */ + static function findManyFiles($pairs) { + $files = array(); + foreach ($pairs as $pair) { + list ($dir, $pattern) = $pair; + $files = array_merge($files, CRM_Utils_File::findFiles($dir, $pattern)); + } + return $files; + } } diff --git a/xml/GenCode.php b/xml/GenCode.php index 3d817ca733..797bad1434 100644 --- a/xml/GenCode.php +++ b/xml/GenCode.php @@ -29,6 +29,7 @@ $genCode = new CRM_Core_CodeGen_Main( array('../packages/Smarty/plugins', '../CRM/Core/Smarty/plugins'), // smarty plugin dirs @$argv[3], // cms empty($argv[2]) ? NULL : $argv[2], // db version - empty($argv[1]) ? 'schema/Schema.xml' : $argv[1] // schem afile + empty($argv[1]) ? 'schema/Schema.xml' : $argv[1], // schem afile + getenv('CIVICRM_GENCODE_DIGEST') ? getenv('CIVICRM_GENCODE_DIGEST') : NULL ); $genCode->main(); -- 2.25.1