Changeset View
Changeset View
Standalone View
Standalone View
arcanist/workflow/ArcanistBuildWorkflow.php
- This file was added.
<?php | |||||
/** | |||||
* Build the project | |||||
*/ | |||||
final class ArcanistBuildWorkflow extends ArcanistWorkflow { | |||||
const RESULT_OKAY = 0; | |||||
const RESULT_WARNINGS = 1; | |||||
const RESULT_ERRORS = 2; | |||||
const RESULT_SKIP = 3; | |||||
private $buildDir; | |||||
public function getWorkflowName() { | |||||
return 'build'; | |||||
} | |||||
public function getCommandSynopses() { | |||||
return phutil_console_format(<<<EOTEXT | |||||
**build** [__targets__] | |||||
EOTEXT | |||||
); | |||||
} | |||||
public function getCommandHelp() { | |||||
return phutil_console_format(<<<EOTEXT | |||||
Build the bitcoin-abc project using CMake and Ninja. | |||||
You can specify the Ninja targets by passing them to __targets__. | |||||
Requires: cmake, ninja | |||||
EOTEXT | |||||
); | |||||
} | |||||
public function getArguments() { | |||||
return array( | |||||
'*' => 'targets', | |||||
); | |||||
} | |||||
public function requiresWorkingCopy() { | |||||
return true; | |||||
} | |||||
private function setSuccess($duration) { | |||||
$out = phutil_console_format("**<bg:green> %s </bg>** %s (%.3f s)\n", | |||||
pht('OKAY'), pht('Build is successful'), $duration); | |||||
PhutilConsole::getConsole()->writeOut($out); | |||||
return self::RESULT_OKAY; | |||||
} | |||||
private function setFailure($failureReason) { | |||||
$out = phutil_console_format( "**<bg:red> %s </bg>** %s\n", pht('FAILED'), | |||||
pht($failureReason)); | |||||
PhutilConsole::getConsole()->writeOut($out); | |||||
return self::RESULT_ERRORS; | |||||
} | |||||
private function build() { | |||||
/* Run CMake from the build repository */ | |||||
$future = new ExecFuture( | |||||
'cmake -GNinja '.$this->getWorkingCopy()->getProjectRoot()); | |||||
$future->setCWD($this->buildDir); | |||||
list($ret) = $future->resolve(); | |||||
if ($ret != 0) { | |||||
return false; | |||||
} | |||||
/* | |||||
* Run Ninja from the build directory. | |||||
* If "targets" arguments are specified, pass them to Ninja, otherwise do | |||||
* not speficy any target and let Ninja build the default. | |||||
*/ | |||||
$targets = $this->getArgument('targets'); | |||||
$cmd = 'ninja'; | |||||
foreach ($targets as $_) { | |||||
$cmd .= ' %s'; | |||||
} | |||||
$future = new ExecFuture($cmd, ...$targets); | |||||
$future->setCWD($this->buildDir); | |||||
list($ret) = $future->resolve(); | |||||
return $ret == 0; | |||||
} | |||||
private function removeBuildDirectory() { | |||||
if (!Filesystem::pathExists($this->buildDir)) { | |||||
return true; | |||||
} | |||||
if (Filesystem::pathsAreEquivalent($this->buildDir, | |||||
$this->getWorkingCopy()->getProjectRoot())) { | |||||
return false; | |||||
} | |||||
try { | |||||
Filesystem::remove($this->buildDir); | |||||
} catch (FilesystemException $e) { | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
public function run() { | |||||
if (!Filesystem::binaryExists('cmake')) { | |||||
return $this->setFailure( | |||||
'The `cmake` command is required to run `arc build`. Please install '. | |||||
'CMake (https://cmake.org) then run `arc build` again.'); | |||||
} | |||||
if (!Filesystem::binaryExists('ninja')) { | |||||
return $this->setFailure( | |||||
'The `ninja` command is required to run `arc build`. Please install '. | |||||
'Ninja (https://ninja-build.org) hen run `arc build` again.'); | |||||
} | |||||
$workingCopy = $this->getWorkingCopy(); | |||||
/* | |||||
* Create the build directory if it doesn't exist. | |||||
* The build directory is read from the option | |||||
* `build_directory` in the `.arcbuild` configuration file. | |||||
* | |||||
* If the directory already exists, remove it first. | |||||
*/ | |||||
/* Find the .arcbuild configuration file. */ | |||||
$configPath = $workingCopy->getProjectPath('.arcbuild'); | |||||
if (!Filesystem::pathExists($configPath)) { | |||||
return $this->setFailure( | |||||
'Unable to find the `.arcbuild` configuration file. Please create the '. | |||||
'file at the root of the project.'); | |||||
} | |||||
/* Read the .arcbuild configuration file, check it is a valid json file. */ | |||||
$configData = Filesystem::readFile($configPath); | |||||
try { | |||||
$config = phutil_json_decode($configData); | |||||
} catch (PhutilJSONParserException $e) { | |||||
return $this->setFailure( | |||||
'The `.arcbuild` file is not a valid JSON file, please check for '. | |||||
'syntax errors.'); | |||||
} | |||||
/* Make sure the `build_directory` option is set. */ | |||||
if (!array_key_exists('build_directory', $config)) { | |||||
return $this->setFailure( | |||||
'No build directory is configured. Set the `build_directory` option '. | |||||
'in your `.arcbuild` configuration file and run `arc build` again.'); | |||||
} | |||||
$root = $workingCopy->getProjectRoot(); | |||||
$this->buildDir = Filesystem::resolvePath($config['build_directory'], | |||||
$root); | |||||
/* Disallow to set the project root as the build directory. */ | |||||
if (Filesystem::pathsAreEquivalent($this->buildDir, $root)) { | |||||
return $this->setFailure( | |||||
'The build directory is set to the project root: '.$this->buildDir.'. '. | |||||
'The directory is removed after the build, please set it to something '. | |||||
'else.'); | |||||
} | |||||
/* If the build directory exists, try to remove it first. */ | |||||
if (!$this->removeBuildDirectory()) { | |||||
return $this->setFailure( | |||||
'The build directory '.$this->buildDir.' already exists and cannot be '. | |||||
'deleted. Please check the permissions or delete it manually then '. | |||||
'run `arc build` again.'); | |||||
} | |||||
/* Create the build directory. */ | |||||
try { | |||||
$this->buildDir = Filesystem::createDirectory($this->buildDir, 0755, | |||||
true); | |||||
} catch (FilesystemException $e) { | |||||
return $this->setFailure( | |||||
'Unable to create the build directory: '.$this->buildDir.'. Check you '. | |||||
'have write permissions to the parent directory and run `arc build` '. | |||||
'again.'); | |||||
} | |||||
/* Build the project, and measure the build duration. */ | |||||
$timeStart = microtime(true); | |||||
if (!$this->build()) { | |||||
return $this->setFailure('Build failed !'); | |||||
} | |||||
$timeEnd = microtime(true); | |||||
return $this->setSuccess($timeEnd - $timeStart); | |||||
} | |||||
public function finalize() { | |||||
$this->removeBuildDirectory(); | |||||
} | |||||
} |