diff --git a/.dockerignore b/.dockerignore index f5d6cbc4e..69375ec74 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,6 +4,7 @@ ci/coverage-report !package.json !binding.gyp !lib +!install !test !cc !data/got.jpg diff --git a/.travis.yml b/.travis.yml index e20295f98..8bb676aa3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -199,7 +199,7 @@ script: fi after_success: - - if [ $BUILD_TASK = 'cover' ]; then + - if [ "$BUILD_TASK" == "cover" ]; then npm install; npm run codecov -- -t $CODECOV_TOKEN; fi diff --git a/README.md b/README.md index 5ae297011..13ba9a0e1 100644 --- a/README.md +++ b/README.md @@ -174,9 +174,40 @@ You can specify the Version of OpenCV you want to install via the script by sett If you only want to build a subset of the OpenCV modules you can pass the *-DBUILD_LIST* cmake flag via the *OPENCV4NODEJS_AUTOBUILD_FLAGS* environment variable. For example `export OPENCV4NODEJS_AUTOBUILD_FLAGS=-DBUILD_LIST=dnn` will build only modules required for `dnn` and reduces the size and compilation time of the OpenCV package. +## Configuring Environments via package.json + +It's possible to specify build environment variables by inserting them into the `package.json` as follows: + +```json +{ + "name": "my-project", + "version": "0.0.0", + "dependencies": { + "opencv4nodejs": "^X.X.X" + }, + "opencv4nodejs": { + "disableAutoBuild": 1, + "opencvIncludeDir": "C:\\tools\\opencv\\build\\include", + "opencvLibDir": "C:\\tools\\opencv\\build\\x64\\vc14\\lib", + "opencvBinDir": "C:\\tools\\opencv\\build\\x64\\vc14\\bin" + } +} +``` + +The following environment variables can be passed: + +- autoBuildBuildCuda +- autoBuildFlags +- autoBuildOpencvVersion +- autoBuildWithoutContrib +- disableAutoBuild +- opencvIncludeDir +- opencvLibDir +- opencvBinDir + -## Usage with Docker +# Usage with Docker ### [opencv-express](https://github.com/justadudewhohacks/opencv-express) - example for opencv4nodejs with express.js and docker @@ -192,7 +223,7 @@ Different OpenCV 3.x base images can be found here: https://hub.docker.com/r/jus -## Usage with Electron +# Usage with Electron ### [opencv-electron](https://github.com/justadudewhohacks/opencv-electron) - example for opencv4nodejs with electron @@ -213,7 +244,7 @@ const cv = require('opencv4nodejs'); -## Usage with NW.js +# Usage with NW.js Any native modules, including opencv4nodejs, must be recompiled to be used with [NW.js](https://nwjs.io/). Instructions on how to do this are available in the **[Use Native Modules](http://docs.nwjs.io/en/latest/For%20Users/Advanced/Use%20Native%20Node%20Modules/)** section of the the NW.js documentation. diff --git a/appveyor.yml b/appveyor.yml index 6d1321fad..7ad8b9a2c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -43,26 +43,33 @@ environment: - nodejs_version: 6 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 OPENCV_VERSION: "%OPENCV4_LATEST%" + - nodejs_version: 12 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + OPENCV_VERSION: "%OPENCV4_LATEST%" + BUILD_TASK: "ENVS" install: - cmd: choco install OpenCV -y -version %OPENCV_VERSION% - - IF EXIST c:\tools\opencv* CD c:\tools\opencv* - - SET OPENCV_INCLUDE_DIR=%CD%\build\include - - SET OPENCV_LIB_DIR=%CD%\build\x64\vc14\lib - - SET OPENCV_BIN_DIR=%CD%\build\x64\vc14\bin - - SET PATH=%PATH%;%OPENCV_BIN_DIR%; - + - if not "%BUILD_TASK%" == "ENVS" SET OPENCV_INCLUDE_DIR=c:\tools\opencv\build\include + - if not "%BUILD_TASK%" == "ENVS" SET OPENCV_LIB_DIR=c:\tools\opencv\build\x64\vc14\lib + - if not "%BUILD_TASK%" == "ENVS" SET OPENCV_BIN_DIR=c:\tools\opencv\build\x64\vc14\bin + - if not "%BUILD_TASK%" == "ENVS" SET PATH=%PATH%;%OPENCV_BIN_DIR%; - ps: Install-Product node $env:nodejs_version x64 - node --version - - npm install -g node-gyp - - cd c:\projects\opencv4nodejs - - npm install build: off test_script: - node --version - - cmd: cd c:\projects\opencv4nodejs\test - - npm install - - npm run test-appveyor - - npm run test-externalMemTracking \ No newline at end of file + - if "%BUILD_TASK%" == "ENVS" ( + cd c:\projects\opencv4nodejs\ci\envs && + npm install && + npm test + ) else ( + cd c:\projects\opencv4nodejs && + npm install && + cd c:\projects\opencv4nodejs\test && + npm install && + npm run test-appveyor && + npm run test-externalMemTracking + ) \ No newline at end of file diff --git a/binding.gyp b/binding.gyp index de2d46b84..a19da5993 100644 --- a/binding.gyp +++ b/binding.gyp @@ -2,17 +2,17 @@ "targets": [{ "target_name": "opencv4nodejs", "defines": [ - " lib.libPath) + +if (!libsFoundInDir.length) { + throw new Error('no OpenCV libraries found in lib dir: ' + libDir) +} + +log.info('install', 'found the following libs:') +libsFoundInDir.forEach(lib => log.info('install', lib.opencvModule + ' : ' + lib.libPath)) + +const defines = libsFoundInDir + .map(lib => `OPENCV4NODEJS_FOUND_LIBRARY_${lib.opencvModule.toUpperCase()}`) + +const explicitIncludeDir = resolvePath(process.env.OPENCV_INCLUDE_DIR) +const includes = opencvBuild.isAutoBuildDisabled() + ? (explicitIncludeDir ? [explicitIncludeDir] : getDefaultIncludeDirs()) + : [resolvePath(opencvBuild.opencvInclude), resolvePath(opencvBuild.opencv4Include)] + +const libs = opencvBuild.isWin() + ? libsFoundInDir.map(lib => resolvePath(lib.libPath)) + // dynamically link libs if not on windows + : ['-L' + libDir] + .concat(libsFoundInDir.map(lib => '-lopencv_' + lib.opencvModule)) + .concat('-Wl,-rpath,' + libDir) + +console.log() +log.info('install', 'setting the following defines:') +defines.forEach(def => log.info('defines', def)) +console.log() +log.info('install', 'setting the following includes:') +includes.forEach(inc => log.info('includes', inc)) +console.log() +log.info('install', 'setting the following libs:') +libs.forEach(lib => log.info('libs', lib)) + +process.env['OPENCV4NODEJS_DEFINES'] = defines.join('\n') +process.env['OPENCV4NODEJS_INCLUDES'] = includes.join('\n') +process.env['OPENCV4NODEJS_LIBRARIES'] = libs.join('\n') + +const flags = process.env.BINDINGS_DEBUG ? '--jobs max --debug' : '--jobs max' +const nodegypCmd = 'node-gyp rebuild ' + flags +log.info('install', `spawning node gyp process: ${nodegypCmd}`) +const child = child_process.exec(nodegypCmd, {}, function(err, stdout, stderr) { + const _err = err || stderr + if (_err) log.error(_err) +}) +child.stdout.pipe(process.stdout) +child.stderr.pipe(process.stderr) \ No newline at end of file diff --git a/install/parseEnv.js b/install/parseEnv.js new file mode 100644 index 000000000..8861cf686 --- /dev/null +++ b/install/parseEnv.js @@ -0,0 +1,7 @@ +const envName = process.argv[2] + +if (!envName) { + throw new Error('no env name passed to parseEnv') +} +const outputs = (process.env[envName] || '').split('\n') +outputs.forEach(o => console.log(o)) \ No newline at end of file diff --git a/lib/commons.js b/lib/commons.js index 9da59b2f0..3ee60bc67 100644 --- a/lib/commons.js +++ b/lib/commons.js @@ -1,29 +1,13 @@ -const fs = require('fs'); -const path = require('path'); +const fs = require('fs') +const path = require('path') function resolvePath(filePath, file) { if (!filePath) { - return undefined; + return undefined } - return (file ? path.resolve(filePath, file) : path.resolve(filePath)).replace(/\\/g, '/'); -} - -const defaultDir = '/usr/local'; -const defaultIncludeDir = `${defaultDir}/include`; -const defaultIncludeDirOpenCV4 = `${defaultIncludeDir}/opencv4`; - -function getLibDir() { - const libPath = resolvePath(process.env.OPENCV_LIB_DIR) - if (process.platform === 'win32' && !libPath) { - throw new Error('OPENCV_LIB_DIR is not defined') - } - return libPath || `${defaultDir}/lib`; + return (file ? path.resolve(filePath, file) : path.resolve(filePath)).replace(/\\/g, '/') } module.exports = { - resolvePath, - defaultDir, - defaultIncludeDir, - defaultIncludeDirOpenCV4, - getLibDir -}; + resolvePath +} diff --git a/lib/cv.js b/lib/cv.js index 44e24dd96..1bfac377f 100644 --- a/lib/cv.js +++ b/lib/cv.js @@ -2,19 +2,60 @@ const path = require('path'); const opencvBuild = require('opencv-build'); const { resolvePath } = require('./commons'); -// ensure binaries are added to path on windows -if (!opencvBuild.isAutoBuildDisabled() && process.platform === 'win32') { - // append opencv binary path to node process - if (!process.env.path.includes(opencvBuild.opencvBinDir)) { - process.env.path = `${process.env.path};${opencvBuild.opencvBinDir};` +const requirePath = path.join(__dirname, process.env.BINDINGS_DEBUG ? '../build/Debug/opencv4nodejs' : '../build/Release/opencv4nodejs') + +const logDebug = process.env.OPENCV4NODES_DEBUG_REQUIRE ? require('npmlog').info : () => {} + +function tryGetOpencvBinDir() { + if (process.env.OPENCV_BIN_DIR) { + logDebug('tryGetOpencvBinDir', 'OPENCV_BIN_DIR environment variable is set') + return process.env.OPENCV_BIN_DIR } + // if the auto build is not disabled via environment do not even attempt + // to read package.json + if (!opencvBuild.isAutoBuildDisabled()) { + logDebug('tryGetOpencvBinDir', 'auto build has not been disabled via environment variable, using opencv bin dir of opencv-build') + return opencvBuild.opencvBinDir + } + + logDebug('tryGetOpencvBinDir', 'auto build has not been explicitly disabled via environment variable, attempting to read envs from package.json...') + const envs = opencvBuild.readEnvsFromPackageJson() + + if (!envs.disableAutoBuild) { + logDebug('tryGetOpencvBinDir', 'auto build has not been disabled via package.json, using opencv bin dir of opencv-build') + return opencvBuild.opencvBinDir + } + + if (envs.opencvBinDir) { + logDebug('tryGetOpencvBinDir', 'found opencv binary environment variable in package.json') + return envs.opencvBinDir + } + logDebug('tryGetOpencvBinDir', 'failed to find opencv binary environment variable in package.json') + return null } -let cv; -if (process.env.BINDINGS_DEBUG) { - cv = require(path.join(__dirname, '../build/Debug/opencv4nodejs')); -} else { - cv = require(path.join(__dirname, '../build/Release/opencv4nodejs')); +let cv = null +try { + logDebug('require', 'require path is ' + requirePath) + cv = require(requirePath); +} catch (err) { + logDebug('require', 'failed to require cv with exception: ' + err.toString()) + logDebug('require', 'attempting to add opencv binaries to path') + + if (!process.env.path) { + logDebug('require', 'there is no path environment variable, skipping...') + throw err + } + + const opencvBinDir = tryGetOpencvBinDir() + logDebug('require', 'adding opencv binary dir to path: ' + opencvBinDir) + + // ensure binaries are added to path on windows + if (!process.env.path.includes(opencvBinDir)) { + process.env.path = `${process.env.path};${opencvBinDir};` + } + logDebug('require', 'process.env.path: ' + process.env.path) + cv = require(requirePath); } // resolve haarcascade files diff --git a/lib/defines.js b/lib/defines.js deleted file mode 100644 index 01c8c0973..000000000 --- a/lib/defines.js +++ /dev/null @@ -1,14 +0,0 @@ -const opencvBuild = require('opencv-build'); -const { getLibDir } = require('./commons'); - -if (opencvBuild.isAutoBuildDisabled()) { - opencvBuild.getLibs(getLibDir()) - .filter(lib => lib.libPath) - .map(lib => lib.opencvModule) - .forEach(opencvModule => console.log(`OPENCV4NODEJS_FOUND_LIBRARY_${opencvModule.toUpperCase()}`)); - - return; -} - -// set defines from auto build -opencvBuild.opencvModules.forEach(m => console.log(`OPENCV4NODEJS_FOUND_LIBRARY_${m.toUpperCase()}`)); \ No newline at end of file diff --git a/lib/includes.js b/lib/includes.js deleted file mode 100644 index ffdc10b6d..000000000 --- a/lib/includes.js +++ /dev/null @@ -1,17 +0,0 @@ -const opencvBuild = require('opencv-build'); -const { resolvePath, defaultIncludeDir, defaultIncludeDirOpenCV4 } = require('./commons'); - -if (opencvBuild.isAutoBuildDisabled()) { - const explicitIncludeDir = resolvePath(process.env.OPENCV_INCLUDE_DIR); - if (explicitIncludeDir) { - console.log(explicitIncludeDir); - return; - } - console.log(defaultIncludeDir); - console.log(defaultIncludeDirOpenCV4); - return; -} - -// set include dir from auto build -console.log(resolvePath(opencvBuild.opencvInclude)); -console.log(resolvePath(opencvBuild.opencv4Include)); diff --git a/lib/libs.js b/lib/libs.js deleted file mode 100644 index 90cdefaed..000000000 --- a/lib/libs.js +++ /dev/null @@ -1,25 +0,0 @@ -const opencvBuild = require('opencv-build'); -const { resolvePath, getLibDir } = require('./commons'); - -function linkLibs(libs) { - libs - .map(lib => lib.libPath) - .filter(libPath => libPath) - .forEach(libPath => console.log(resolvePath(libPath))); -} - -if (opencvBuild.isAutoBuildDisabled()) { - linkLibs(opencvBuild.getLibs(getLibDir())); - return; -} - -// get libs from auto build -if (process.platform === 'win32') { - linkLibs(opencvBuild.getLibs(resolvePath(opencvBuild.opencvLibDir))); - return; -} - -// if not windows, link libs dynamically -console.log('-L' + resolvePath(opencvBuild.opencvLibDir)); -opencvBuild.opencvModules.forEach(lib => console.log('-lopencv_' + lib)); -console.log('-Wl,-rpath,' + resolvePath(opencvBuild.opencvLibDir)); diff --git a/package-lock.json b/package-lock.json index db67b0595..aa2654ddc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -122,19 +122,11 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "opencv-build": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/opencv-build/-/opencv-build-0.1.5.tgz", - "integrity": "sha512-KcK3+EwgPowTMogTxC5+iZgn8EraQlof339EIV3dupbMqBWe2ZgWzQDEGlt00DgMPmcFkn0+bSm4ifuQSevGow==", + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/opencv-build/-/opencv-build-0.1.9.tgz", + "integrity": "sha512-tgT/bnJAcYROen9yaPynfK98IMl62mPSgMLmTx41911m5bczlq21xtE5r+UWLB/xEo/0hKk6tl5zHyxV/JS5Rg==", "requires": { - "@types/node": "^11.10.5", "npmlog": "^4.1.2" - }, - "dependencies": { - "@types/node": { - "version": "11.13.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.19.tgz", - "integrity": "sha512-tLRDU1hmcWamtgRT2iVRdraAQVGFQGgtcqracSo9XyMN1VeZLSVGb8RJJxVqab7UGbijoUijGPVFMjmqzyZIUw==" - } } }, "process-nextick-args": { diff --git a/package.json b/package.json index cc9f3bf90..7ded66460 100644 --- a/package.json +++ b/package.json @@ -29,18 +29,19 @@ "main": "./lib/opencv4nodejs.js", "typings": "./lib/index.d.ts", "scripts": { - "install": "node-gyp rebuild --jobs max", + "install": "node ./install/install.js", "configure": "node-gyp configure", "build": "node-gyp configure build --jobs max", "rebuild": "node-gyp rebuild --jobs max", "clean": "node-gyp clean", - "build-debug": "node-gyp rebuild --debug --jobs max" + "build-debug": "BINDINGS_DEBUG=true node ./install/install.js" }, "gypfile": true, "dependencies": { "nan": "^2.14.0", "native-node-utils": "^0.2.7", - "opencv-build": "^0.1.5" + "npmlog": "^4.1.2", + "opencv-build": "^0.1.9" }, "optionalDependencies": { "@types/node": ">6" diff --git a/test/tests/index.test.js b/test/tests/index.test.js index ebd2b9d36..46c45ef44 100644 --- a/test/tests/index.test.js +++ b/test/tests/index.test.js @@ -64,9 +64,25 @@ describe('cv', () => { builtModules = builtModules.filter(m => m !== 'dnn') } + const opencvVersionString = `${cv.version.major}.${cv.version.minor}.${cv.version.revision}` + + console.log('envs are:') + console.log('OPENCV_VERSION:', process.env.OPENCV_VERSION) + console.log('TEST_MODULE_LIST:', process.env.TEST_MODULE_LIST) + console.log('APPVEYOR_BUILD:', process.env.APPVEYOR_BUILD) + console.log('process.platform:', process.platform) + console.log() + console.log('OpenCV version is:', opencvVersionString) console.log('compiled with the following modules:', cv.modules) console.log('expected modules to be built:', builtModules) + it('OpenCV version should match', () => { + expect((process.env.OPENCV_VERSION || '').substr(0, 5)).to.equal( + // on osx latest opencv package for major version is installed via brew + process.platform === 'darwin' ? `${cv.version.major}` : opencvVersionString + ) + }) + it('all modules should be built', () => { builtModules.forEach(m => expect(cv.modules).to.have.property(m)); })