const fs = require('fs');
const path = require('path');
const shell = require('shelljs');
const process = require("process");
const requiredCMakeVersion = '3.15';
const cmakeBuildType = 'Release';
shell.config.fatal = true; // thrown an exception on any error
let commonEnvVariables = {
CMAKE_BUILD_TYPE: cmakeBuildType,
CMAKE_INSTALL_PREFIX: '${PWD}/bin',
BUILD_SHARED_LIBS: 'OFF',
CMAKE_POSITION_INDEPENDENT_CODE: 'ON',
CMAKE_MSVC_RUNTIME_LIBRARY: 'MultiThreaded',
CMAKE_POLICY_DEFAULT_CMP0091: 'NEW',
CMAKE_OSX_DEPLOYMENT_TARGET: '10.9'
}
const buildForArch = process.env["BUILD_FOR_ARCH"] || process.arch;
shell.echo('buildForArch', buildForArch);
if (buildForArch === 'arm64') {
shell.echo('arm64 build');
commonEnvVariables = {
...commonEnvVariables,
CMAKE_OSX_ARCHITECTURES: '\"arm64\"'
}
} else if (buildForArch === 'x64') {
shell.echo('x64 build');
commonEnvVariables = {
...commonEnvVariables,
CMAKE_OSX_ARCHITECTURES: '\"x86_64\"'
}
}
// ------ startup ------
shell.echo('build-tesseract script start.');
if (!shell.which('git')) {
shell.echo('This script requires Git.');
shell.exit(1);
}
checkCMakeVersion();
const homeDir = path.resolve(__dirname,'..');
shell.cd(homeDir);
shell.echo(`Working directory: ${homeDir}`);
// ------ libraries ------
buildLibjpeg ('libjpeg');
buildLeptonica ('leptonica');
buildTesseract ('tesseract');
shell.echo('build-tesseract script end.');
function checkCMakeVersion() {
let versionOK = false;
shell.echo(`This script requires CMake version ${requiredCMakeVersion} or later.`);
if (!shell.which('cmake')) {
shell.echo('CMake not found on this system.');
} else {
const reply = shell.exec('cmake --version', {silent: true});
foundVersion = (/\d+.\d+.\d+/mg).exec(reply)[0];
versionOK = checkVersion(foundVersion, requiredCMakeVersion) >= 0;
if (versionOK) {
shell.echo(`CMake ${foundVersion} found on this system.`);
} else {
shell.echo(`CMake ${foundVersion} found on this system is too old.`);
}
}
if (!versionOK) shell.exit(1);
}
// https://codereview.stackexchange.com/questions/236647/comparing-version-numbers-with-javascript
function checkVersion (a, b) {
const x = a.split('.').map(e => parseInt(e, 10));
const y = b.split('.').map(e => parseInt(e, 10));
for (const i in x) {
y[i] = y[i] || 0;
if (x[i] === y[i]) {
continue;
} else if (x[i] > y[i]) {
return 1;
} else {
return -1;
}
}
return y.length > x.length ? -1 : 0;
}
function buildLibjpeg (dirName) {
shell.echo('Building libjpeg.');
if (shell.test('-e', dirName)) {
shell.echo(`The ${dirName} directory already exists.`);
} else {
shell.exec(`git clone https://github.com/tamaskenez/libjpeg-cmake.git ${dirName}`);
}
runCMakeBuild (dirName, cmakeBuildType);
}
function buildLeptonica (dirName) {
shell.echo('Building Leptonica.');
runCMakeBuild (dirName, cmakeBuildType,
{
SW_BUILD: 'OFF',
CMAKE_FIND_USE_CMAKE_SYSTEM_PATH: 'FALSE',
CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH: process.platform === 'darwin' ? 'FALSE' : 'TRUE',
CMAKE_PREFIX_PATH: '${PWD}/../../libjpeg/build',
CMAKE_INCLUDE_PATH: '${PWD}/../../libjpeg/build/bin/include',
CMAKE_LIBRARY_PATH: '${PWD}/../../libjpeg/build/bin/lib'
},
(dirName, cmakeConfig, envVars) => { // patch config_auto.h between config and build
if (process.platform === 'darwin') {
const filePath = path.resolve(__dirname,'..',dirName,'build','src','config_auto.h');
shell.echo(`Patching ${filePath} for Mac.`);
let autoConfig = fs.readFileSync(filePath, 'utf8');
const searchText = /^#define\s+HAVE_FMEMOPEN\s+1/gm;
const replacementText = '#define HAVE_FMEMOPEN 0';
const foundText = autoConfig.match(searchText);
if (foundText) {
const updatedConfig = autoConfig.replace(searchText, replacementText);
fs.writeFileSync(filePath, updatedConfig, 'utf8');
shell.echo(`The '${foundText}' directive was replaced with '${replacementText}'.`);
} else {
shell.echo(`The '#define HAVE_FMEMOPEN 1' directive was not found.`);
shell.echo('This may lead to a build that does not run on all macOS machines.');
}
}
}
);
}
function buildTesseract (dirName) {
shell.echo('Building Tesseract.');
runCMakeBuild (dirName, cmakeBuildType,
{
STATIC: 'ON',
CPPAN_BUILD: 'OFF',
BUILD_TRAINING_TOOLS: 'OFF',
AUTO_OPTIMIZE: 'OFF',
Leptonica_DIR: '../leptonica/build'
}
);
}
function runCMakeBuild (dirName, cmakeBuildType, envVars, patchConfig) {
createAndEnterBuildDir(dirName);
let cmakeCmd = 'cmake';
cmakeCmd += formatEnvVars (envVars);
cmakeCmd += formatEnvVars (commonEnvVariables);
cmakeCmd += ' ..';
shell.echo(`Configuring a ${cmakeBuildType} build.`)
shell.echo(cmakeCmd);
shell.exec(cmakeCmd);
if (patchConfig) patchConfig(dirName, cmakeBuildType, envVars);
shell.echo(`Creating a ${cmakeBuildType} build.`)
shell.exec(`cmake --build . --config ${cmakeBuildType}`);
shell.echo(`Installing a ${cmakeBuildType} build.`)
shell.exec('cmake --install .');
leaveBuildDir();
}
function createAndEnterBuildDir (dirName) {
shell.pushd('-q', dirName);
if (!shell.test('-e', 'build')) shell.mkdir('build');
shell.pushd('-q', 'build');
}
function leaveBuildDir() {
shell.popd('-q');
shell.popd('-q');
}
function formatEnvVars (envVars) {
const continuation = process.platform === 'win32' ? '' : ' \\\n';
let args = '';
for (key in envVars) {
args += ` -D${key}=${envVars[key]}${continuation}`;
}
return args;
}
|