├── .gitignore ├── .npmignore ├── .vscode └── settings.json ├── deps.ts ├── dev_deps.ts ├── main.ts ├── deno.json ├── src ├── utils_test.ts ├── types.ts ├── misc_test.ts ├── constants.ts ├── env.ts ├── Log.ts ├── findMsBuild.ts ├── getLibsFactory.ts ├── OpenCVBuilder_test.ts ├── helper │ └── detect.ts ├── OpenCVBuilder.ts ├── StaticTools.ts ├── utils.ts ├── setupOpencv.ts ├── misc.ts └── OpenCVBuildEnv.ts ├── .github └── workflows │ ├── linux-build.yml │ ├── mac-detect.yml │ ├── linux-detect.yml │ ├── w64-detect.yml │ └── vcpkg-detect.yml ├── mod.ts ├── appveyor.yml.old ├── .travis.yml ├── CHANGELOG.md ├── _build_npm.ts └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | opencv-*/ 3 | npm/ 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | opencv 2 | node_modules 3 | .vscode 4 | test -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true 3 | } 4 | -------------------------------------------------------------------------------- /deps.ts: -------------------------------------------------------------------------------- 1 | export * as pc from "jsr:@std/fmt/colors"; 2 | // export * as pc from "https://deno.land/std@0.223.0/fmt/colors.ts"; 3 | export * as path from "jsr:@std/path"; 4 | -------------------------------------------------------------------------------- /dev_deps.ts: -------------------------------------------------------------------------------- 1 | // export * as pc from "https://deno.land/std@0.223.0/fmt/colors.ts"; 2 | // export * as pc from "jsr:@std/fmt/colors"; 3 | export * as pc from "jsr:@std/fmt/colors"; 4 | export { assert, assertEquals } from "jsr:@std/assert"; 5 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import * as OpenCVBuilder from "./src/OpenCVBuilder.ts"; 2 | export { pc } from "./deps.ts"; 3 | // Learn more at https://deno.land/manual/examples/module_metadata#concepts 4 | // if (import.meta.main) 5 | const builder = new OpenCVBuilder.OpenCVBuilder(Deno.args); 6 | void builder.install(); 7 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run --watch main.ts" 4 | }, 5 | "imports": { 6 | "@deno/dnt": "jsr:@deno/dnt@^0.41.3", 7 | "@denodnt/logger": "jsr:@deno-lib/logger@^1.1.6", 8 | "@std/assert": "jsr:@std/assert@^1.0.2", 9 | "@std/fmt": "jsr:@std/fmt@^1.0.0" 10 | }, 11 | "node": true, 12 | "lock": false 13 | } 14 | -------------------------------------------------------------------------------- /src/utils_test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "../dev_deps.ts"; 2 | import * as utils from "./utils.ts"; 3 | 4 | Deno.test("protect", function testLib() { 5 | assert(utils.protect("a") === "a"); 6 | }); 7 | 8 | Deno.test("toExecCmd", function testLib() { 9 | assert(utils.toExecCmd("a", ["b", "c"]) === "a b c"); 10 | }); 11 | 12 | Deno.test("exec", async function testLib() { 13 | const result = await utils.exec("echo foo"); 14 | assert(result.trim() === "foo", `result value is "${result}"`); 15 | }); 16 | 17 | Deno.test("execSync", function testLib() { 18 | const result = utils.execSync("echo foo"); 19 | assert(result.trim() === "foo", `result value is "${result}"`); 20 | }); 21 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { OpencvModulesType } from "./misc.ts"; 2 | 3 | export type OpencvModule = { 4 | opencvModule: string; 5 | libPath: string | undefined; 6 | }; 7 | 8 | export interface EnvSummery { 9 | opencvVersion: string; 10 | buildWithCuda: boolean; 11 | isWithoutContrib: boolean; 12 | isAutoBuildDisabled: boolean; 13 | buildRoot: string; 14 | cudaArch: string; 15 | autoBuildFlags: string; 16 | OPENCV_INCLUDE_DIR: string; 17 | OPENCV_LIB_DIR: string; 18 | OPENCV_BIN_DIR: string; 19 | modules: OpencvModulesType[]; 20 | } 21 | 22 | export type AutoBuildFile = { 23 | opencvVersion: string; 24 | autoBuildFlags: string; 25 | modules: OpencvModule[]; 26 | env: EnvSummery; 27 | }; 28 | -------------------------------------------------------------------------------- /.github/workflows/linux-build.yml: -------------------------------------------------------------------------------- 1 | name: build openCV on ubuntu 2 | 3 | on: 4 | push: 5 | branches: [ "master", "piercus", "test", "deno" ] 6 | paths: ['src/**', 'deno.json', '.github/workflows/linux-build.yml' ] 7 | pull_request: 8 | branches: [ "master", "piercus" ] 9 | paths: ['src/**', 'deno.json', '.github/workflows/linux-build.yml' ] 10 | 11 | #env: 12 | # OPENCV4NODEJS_AUTOBUILD_OPENCV_VERSION : 4.7.0 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: denoland/setup-deno@v1 19 | with: 20 | deno-version: v1.x 21 | - name: deno run -A main.ts --version 4.10.0 22 | run: deno run -A main.ts --version 4.10.0 23 | - run: deno test -A 24 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | import OpenCVBuilder from "./src/OpenCVBuilder.ts"; 2 | export type { AutoBuildFile, OpencvModule } from "./src/types.ts"; 3 | export { default as OpenCVBuilder } from "./src/OpenCVBuilder.ts"; 4 | export { getLibsFactory } from "./src/getLibsFactory.ts"; 5 | export { default as OpenCVBuildEnv } from "./src/OpenCVBuildEnv.ts"; 6 | export { 7 | ALL_OPENCV_MODULES, 8 | ALLARGS, 9 | args2Option, 10 | genHelp, 11 | type OpenCVBuildEnvParams, 12 | type OpencvModulesType, 13 | } from "./src/misc.ts"; 14 | export { default as Log } from "./src/Log.ts"; 15 | export type { LogLevels } from "./src/Log.ts"; 16 | export { default as StaticTools } from "./src/StaticTools.ts"; 17 | export { Platfrm } from "./src/env.ts"; 18 | export { pc } from "./deps.ts"; 19 | 20 | export default OpenCVBuilder; 21 | -------------------------------------------------------------------------------- /src/misc_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "../dev_deps.ts"; 2 | import { args2Option } from "./misc.ts"; 3 | 4 | Deno.test("utils", () => { 5 | Deno.test("args2Option should parse standalone unknown bool flags", () => { 6 | const env = args2Option(["--FOO"]); 7 | if (env.extra) { 8 | assertEquals(env.extra.FOO, "1"); 9 | } 10 | }); 11 | 12 | Deno.test("args2Option should parse unknown flags with value", () => { 13 | const env = args2Option(["--FOO", "bar"]); 14 | if (env.extra) { 15 | assertEquals(env.extra.FOO, "bar"); 16 | } 17 | }); 18 | 19 | Deno.test("args2Option should parse unknown string flags with value", () => { 20 | const env = args2Option(["--FOO=bar"]); 21 | if (env.extra) { 22 | assertEquals(env.extra.FOO, "bar"); 23 | } 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /.github/workflows/mac-detect.yml: -------------------------------------------------------------------------------- 1 | name: Use OpenCV from Brew 2 | on: 3 | push: 4 | branches: [ "never", "master", "deno" ] 5 | paths: ['src/**', 'deno.json', '*.ts', '.github/workflows/mac-detect.yml' ] 6 | # pull_request: 7 | # branches: [ "master" ] 8 | jobs: 9 | build: 10 | 11 | runs-on: macos-latest 12 | 13 | strategy: 14 | matrix: 15 | # node-version: [14.x, 16.x, 18.x] 16 | opencv_version: 17 | - 4 18 | 19 | env: 20 | OPENCV4NODEJS_DISABLE_AUTOBUILD: 1 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: denoland/setup-deno@v1 24 | with: 25 | deno-version: v1.x 26 | - name: Install OpenCV ${{ matrix.opencv_version }} 27 | run: | 28 | brew install opencv@${{ matrix.opencv_version }} 29 | - name: Detect OpenCV 30 | run: deno run -A main.ts --verbose 31 | - run: deno test -A 32 | -------------------------------------------------------------------------------- /.github/workflows/linux-detect.yml: -------------------------------------------------------------------------------- 1 | name: Use OpenCV from Ubuntu apt 2 | on: 3 | push: 4 | branches: [ "never", "master", "deno" ] 5 | paths: ['src/**', 'deno.json', 'src/*.ts', '.github/workflows/linux-detect.yml' ] 6 | # pull_request: 7 | # branches: [ "master" ] 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | # node-version: [14.x, 16.x, 18.x] 16 | opencv_version: 17 | - 4 18 | 19 | env: 20 | OPENCV4NODEJS_DISABLE_AUTOBUILD: 1 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: denoland/setup-deno@v1 24 | with: 25 | deno-version: v1.x 26 | - name: Install OpenCV ${{ matrix.opencv_version }} 27 | run: | 28 | sudo apt update; sudo apt install libopencv-dev 29 | - name: Detect OpenCV 30 | run: deno run -A main.ts --verbose 31 | - run: deno test -A 32 | -------------------------------------------------------------------------------- /.github/workflows/w64-detect.yml: -------------------------------------------------------------------------------- 1 | name: Use openCV from Chocolatey 2 | on: 3 | push: 4 | branches: [ "never", "test", "deno" ] 5 | paths: ['src/**', 'deno.json', '*.ts', '.github/workflows/w64-detect.yml' ] 6 | # pull_request: 7 | # branches: [ "master" ] 8 | jobs: 9 | build: 10 | 11 | runs-on: windows-latest 12 | 13 | strategy: 14 | matrix: 15 | # node-version: [14.x, 16.x, 18.x] 16 | opencv_version: 17 | - 4.10.0 18 | - 4.6.0 19 | env: 20 | OPENCV4NODEJS_DISABLE_AUTOBUILD: 1 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: denoland/setup-deno@v1 24 | with: 25 | deno-version: v1.x 26 | - name: Install OpenCV ${{ matrix.opencv_version }} 27 | run: | 28 | choco install OpenCV -y --version ${{ matrix.opencv_version }} 29 | - name: Detect OpenCV 30 | run: deno run -A main.ts --verbose 31 | - run: deno test -A 32 | -------------------------------------------------------------------------------- /appveyor.yml.old: -------------------------------------------------------------------------------- 1 | # platform: 2 | # - x64 3 | # 4 | # image: 5 | # #- Visual Studio 2017 6 | # #- Visual Studio 2019 7 | # - Visual Studio 2022 8 | # 9 | # init: 10 | # - git config --global core.autocrlf true 11 | # shallow_clone: true 12 | # environment: 13 | # matrix: 14 | # #- nodejs_version: 14 15 | # - nodejs_version: 16 16 | # 17 | # clone_folder: c:\projects\opencv-build 18 | # 19 | # install: 20 | # # no need to update cmake on "Visual Studio 2019" image 21 | # # - cmd: choco upgrade --yes chocolatey 22 | # - cmd: choco install cmake --version 3.23.0 -y 23 | # - ps: Install-Product node $env:nodejs_version x64 24 | # - node --version 25 | # - SET PATH=%PATH%;C:\Program Files\CMake\bin; 26 | # 27 | # build: off 28 | # 29 | # test_script: 30 | # # install and run tests of opencv-build 31 | # - cd c:\projects\opencv-build 32 | # - npm install 33 | # - npm link 34 | # - opencv-build-npm --version 4.5.5 35 | # - npm test 36 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | import type { OpenCVBuilder } from "./OpenCVBuilder.ts"; 2 | 3 | export class Constant { 4 | constructor(private readonly builder: OpenCVBuilder) { 5 | } 6 | 7 | opencvRepoUrl = "https://github.com/opencv/opencv.git"; 8 | // opencvRepoUrl = 'c:/cache/opencv' 9 | 10 | opencvContribRepoUrl = "https://github.com/opencv/opencv_contrib.git"; 11 | // opencvContribRepoUrl = 'c:/cache/opencv_contrib' 12 | 13 | // opencvModules = opencvModules; 14 | 15 | cmakeVsCompilers: { [version: string]: string } = { 16 | "10": "Visual Studio 10 2010", 17 | "11": "Visual Studio 11 2012", 18 | "12": "Visual Studio 12 2013", 19 | "14": "Visual Studio 14 2015", 20 | "15": "Visual Studio 15 2017", 21 | "16": "Visual Studio 16 2019", 22 | "17": "Visual Studio 17 2022", 23 | }; 24 | cmakeArchs: { [arch: string]: string } = { 25 | "x64": " Win64", 26 | "ia32": "", 27 | "arm": " ARM", 28 | "x86_64": " Win64", 29 | "aarch64": " ARM", 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/vcpkg-detect.yml: -------------------------------------------------------------------------------- 1 | name: Use openCV from vcpkg 2 | on: 3 | push: 4 | branches: [ "never", "test", "master" ] 5 | paths: ['src/**', 'deno.json', '*.ts', '.github/workflows/vcpkg-detect.yml' ] 6 | # pull_request: 7 | # branches: [ "master" ] 8 | jobs: 9 | build: 10 | 11 | runs-on: windows-latest 12 | 13 | strategy: 14 | matrix: 15 | # node-version: [14.x, 16.x, 18.x] 16 | opencv_version: 17 | - 4.8.0 # Do not known how to choose custom version using vcpkg 18 | env: 19 | OPENCV4NODEJS_DISABLE_AUTOBUILD: 1 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: denoland/setup-deno@v1 23 | with: 24 | deno-version: v1.x 25 | 26 | - name: Install OpenCV ${{ matrix.opencv_version }} 27 | uses: brinkqiang/vcpkg-action@v1 28 | id: vcpkg 29 | with: 30 | pkgs: opencv 31 | triplet: x64-windows-release 32 | token: ${{ github.token }} 33 | github-binarycache: true 34 | 35 | - name: vcpkg install opencv 36 | run: | 37 | vcpkg install opencv 38 | 39 | #- name: Install OpenCV ${{ matrix.opencv_version }} 40 | # run: | 41 | # vcpkg install opencv 42 | - name: Detect OpenCV 43 | run: deno run -A main.ts --verbose 44 | - run: deno test -A 45 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | sudo: required 4 | 5 | matrix: 6 | include: 7 | # default OpenCV Version (3.4.6) 8 | - os: linux 9 | node_js: 16 10 | # OpenCV 4.1.0 11 | - os: linux 12 | node_js: 16 13 | env: 14 | - OPENCV4NODEJS_AUTOBUILD_OPENCV_VERSION=4.6.0 15 | 16 | before_install: 17 | - sudo apt-get update 18 | # install tools for reading images from disk 19 | - sudo apt-get install libjpeg8-dev libtiff5-dev libjasper-dev libpng12-dev 20 | # install video codecs 21 | - sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev 22 | - sudo apt-get install libxvidcore-dev libx264-dev 23 | # install cmake 24 | - wget http://www.cmake.org/files/v3.4/cmake-3.4.1.tar.gz 25 | - tar -xvzf cmake-3.4.1.tar.gz 26 | - cd cmake-3.4.1/ 27 | - ./configure 28 | - make 29 | - cd .. 30 | - cmake --version 31 | 32 | install: 33 | # install and run tests of opencv-build 34 | - npm install 35 | - npm test 36 | 37 | # install opencv4nodejs and run tests 38 | - git clone https://github.com/justadudewhohacks/opencv4nodejs 39 | - cd opencv4nodejs 40 | - npm test 41 | # uninstall npm version of opencv-build 42 | - npm uninstall opencv-build 43 | # install new opencv-build version 44 | - npm install ../ 45 | - npm install 46 | - cd test 47 | - npm install 48 | - npm run test-docker 49 | 50 | -------------------------------------------------------------------------------- /src/env.ts: -------------------------------------------------------------------------------- 1 | import { path } from "../deps.ts"; 2 | 3 | /** 4 | * portable env functions 5 | */ 6 | 7 | export function getEnv(name: string): string { 8 | if (!name) { 9 | return ""; 10 | } 11 | // const value = process.env[name]; 12 | const value = Deno.env.get(name); 13 | return value || ""; 14 | } 15 | 16 | export function setEnv(name: string, value: string): void { 17 | // process.env[name] = value; 18 | Deno.env.set(name, value); 19 | } 20 | 21 | export function getDirname(): string { 22 | // return __dirname if it's a nodejs script 23 | // if (typeof __dirname !== "undefined") { 24 | // return __dirname; 25 | // } 26 | // return import.meta.url if it's a module 27 | const __dirname = path.dirname(path.fromFileUrl(import.meta.url)); 28 | return __dirname; // new URL(".", import.meta.url).pathname; 29 | } 30 | 31 | export class Platfrm { 32 | public static theOS: string = Deno.build.os; // process.platform; 33 | 34 | public static changeOS(os: "windows" | "linux" | "darwin" | string) { 35 | Platfrm.theOS = os; 36 | } 37 | public static get isWindows() { 38 | return Platfrm.theOS.startsWith("win"); // === 'windows'; 39 | } 40 | public static get isLinux() { 41 | return Platfrm.theOS === "linux"; 42 | } 43 | public static get isMac() { 44 | return Platfrm.theOS === "darwin"; 45 | } 46 | } 47 | 48 | export function getArch(): string { 49 | // return process.arch; 50 | return Deno.build.arch; 51 | } 52 | -------------------------------------------------------------------------------- /src/Log.ts: -------------------------------------------------------------------------------- 1 | import Logger from "@denodnt/logger"; 2 | 3 | export type LogLevels = "silly" | "verbose" | "info" | "warn" | "error"; 4 | export const logger = new Logger(); 5 | 6 | export default class Log { 7 | public static silence: boolean; 8 | 9 | public static log( 10 | level: LogLevels | string, 11 | prefix: string, 12 | message: string, 13 | ...args: unknown[] 14 | ): void { 15 | if (!Log.silence) { 16 | switch (level) { 17 | case "info": 18 | logger.info(prefix, message, ...args); 19 | break; 20 | case "warn": 21 | logger.warn(prefix, message, ...args); 22 | break; 23 | case "error": 24 | logger.error(prefix, message, ...args); 25 | break; 26 | default: 27 | logger.info(prefix, message, ...args); 28 | break; 29 | } 30 | // log.log(level, prefix, message, ...args); 31 | } 32 | } 33 | 34 | info(...args: unknown[]): Promise { 35 | if (Log.silence) { 36 | return Promise.resolve(); 37 | } 38 | return logger.info(...args); 39 | } 40 | 41 | warn(...args: unknown[]): Promise { 42 | if (Log.silence) { 43 | return Promise.resolve(); 44 | } 45 | return logger.warn(...args); 46 | } 47 | 48 | error(...args: unknown[]): Promise { 49 | if (Log.silence) { 50 | return Promise.resolve(); 51 | } 52 | return logger.error(...args); 53 | } 54 | 55 | silly(...args: unknown[]): Promise { 56 | if (Log.silence) { 57 | return Promise.resolve(); 58 | } 59 | return logger.info(...args); 60 | } 61 | 62 | verbose(...args: unknown[]): Promise { 63 | if (Log.silence) { 64 | return Promise.resolve(); 65 | } 66 | return logger.info(...args); 67 | } 68 | 69 | public static set level(level: LogLevels) { 70 | // npmlog.level = level; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/findMsBuild.ts: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | import { globSync } from "npm:glob"; 3 | import Log from "./Log.ts"; 4 | import { execFile, formatNumber, highlight, light } from "./utils.ts"; 5 | import { getEnv } from "./env.ts"; 6 | 7 | export interface PathVersion { 8 | version: number; 9 | path: string; 10 | } 11 | 12 | /** 13 | * @returns all MSBuild.exe version in PROGRAM FILES most recent first. 14 | */ 15 | export async function findMSBuild(): Promise { 16 | const progFiles = new Set([ 17 | getEnv("programfiles"), 18 | getEnv("ProgramW6432"), 19 | getEnv("programfiles(x86)"), 20 | ]); 21 | const matches: string[] = []; 22 | 23 | for (const progFile of progFiles) { 24 | if (progFile) { 25 | const reg = `${ 26 | progFile.replace(/\\/g, "/") 27 | }/Microsoft Visual Studio/*/*/MSBuild/*/Bin/MSBuild.exe`; 28 | for (const m of globSync(reg)) { 29 | matches.push(path.resolve(m)); 30 | } 31 | } 32 | } 33 | matches.sort(); 34 | if (!matches.length) { 35 | return Promise.reject( 36 | "no Microsoft Visual Studio found in program files directorys", 37 | ); 38 | } 39 | if (matches.length > 1) { 40 | Log.log( 41 | "warn", 42 | "find-msbuild", 43 | `find ${formatNumber(matches.length)} MSBuild version: [${ 44 | matches.map((path) => light(path)).join(", ") 45 | }]`, 46 | ); 47 | } 48 | const pbuilds = matches.map(async (selected: string) => { 49 | Log.log("silly", "find-msbuild", matches.join(", ")); 50 | // const selected = matches[matches.length - 1]; 51 | const txt = await execFile(selected, ["/version"]); 52 | const m = txt.match(/(\d+)\.\d+/); 53 | if (!m) { 54 | Log.log( 55 | "warn", 56 | "find-msbuild", 57 | `${selected} is not a valid msbuild path, can not find it's versdion`, 58 | ); 59 | return { 60 | path: selected, 61 | version: 0, 62 | }; 63 | } 64 | // return Promise.reject('fail to get MSBuild.exe version number'); 65 | Log.log( 66 | "info", 67 | "find-msbuild", 68 | `discover msbuild v${formatNumber("%s")} in ${highlight("%s")}`, 69 | m[1], 70 | selected, 71 | ); 72 | return { 73 | path: selected, 74 | version: Number(m[1]), 75 | }; 76 | }); 77 | const builds = await Promise.all(pbuilds); 78 | // drop versionnumber = 0; 79 | return builds.filter((a) => a.version).reverse(); 80 | } 81 | -------------------------------------------------------------------------------- /src/getLibsFactory.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | import type OpenCVBuilder from "./OpenCVBuilder.ts"; 4 | import type { OpencvModule } from "./types.ts"; 5 | import type { OpencvModulesType } from "./misc.ts"; 6 | import { Platfrm } from "./env.ts"; 7 | 8 | export class getLibsFactory { 9 | libFiles: string[] = []; 10 | syncPath = true; 11 | 12 | constructor(private builder: OpenCVBuilder) { 13 | } 14 | 15 | /** 16 | * list en cache file in lib folder 17 | * @returns files in lib directory 18 | */ 19 | private listFiles(): string[] { 20 | if (this.libFiles && this.libFiles.length) { 21 | return this.libFiles; 22 | } 23 | const libDir = this.builder.env.opencvLibDir; 24 | this.libFiles = fs.readdirSync(libDir); 25 | return this.libFiles; 26 | } 27 | 28 | /** 29 | * lib files are prefixed differently on Unix / Windows base system. 30 | * @returns current OS prefix 31 | */ 32 | get getLibPrefix(): string { 33 | return Platfrm.isWindows ? "opencv_" : "libopencv_"; 34 | } 35 | 36 | /** 37 | * @returns lib extention based on current OS 38 | */ 39 | get getLibSuffix(): "lib" | "dylib" | "so" { 40 | if (Platfrm.isWindows) { 41 | return "lib"; 42 | } 43 | if (Platfrm.isMac) { 44 | return "dylib"; 45 | } 46 | return "so"; 47 | } 48 | 49 | /** 50 | * build a regexp matching os lib file 51 | * @param opencvModuleName 52 | * @returns 53 | */ 54 | getLibNameRegex(opencvModuleName: string): RegExp { 55 | const regexp = 56 | `^${this.getLibPrefix}${opencvModuleName}[0-9.]*\\.${this.getLibSuffix}$`; 57 | return new RegExp(regexp); 58 | } 59 | 60 | /** 61 | * find a lib 62 | */ 63 | public resolveLib(opencvModuleName: OpencvModulesType): string { 64 | const libDir = this.builder.env.opencvLibDir; 65 | const libFiles: string[] = this.listFiles(); 66 | return this.matchLib(opencvModuleName, libDir, libFiles); 67 | } 68 | /** 69 | * Match lib file names in a folder, was part of resolveLib, but was splitted for easy testing 70 | * @param opencvModuleName openCV module name 71 | * @param libDir library directory 72 | * @param libFiles files in lib directory 73 | * @returns full path to looked up lib file 74 | */ 75 | public matchLib( 76 | opencvModuleName: string, 77 | libDir: string, 78 | libFiles: string[], 79 | ): string { 80 | const regexp = this.getLibNameRegex(opencvModuleName); 81 | const match = libFiles.find((libFile: string) => 82 | !!(libFile.match(regexp) || [])[0] 83 | ); 84 | if (!match) { 85 | return ""; 86 | } 87 | let fullpath = path.resolve(libDir, match); 88 | if (this.syncPath) { 89 | fullpath = fs.realpathSync(fullpath); 90 | } 91 | return fullpath; 92 | } 93 | 94 | getLibs(): OpencvModule[] { 95 | const libDir = this.builder.env.opencvLibDir; 96 | // console.log("libDir => ", libDir); 97 | if (!fs.existsSync(libDir)) { 98 | throw new Error(`specified lib dir does not exist: ${libDir}`); 99 | } 100 | 101 | const modules: OpencvModule[] = []; 102 | const worldModule = "world"; 103 | const worldLibPath = this.resolveLib(worldModule); 104 | if (worldLibPath) { 105 | modules.push({ 106 | opencvModule: worldModule, 107 | libPath: worldLibPath, 108 | }); 109 | } 110 | 111 | const extra = [...this.builder.env.enabledModules].map( 112 | (opencvModule: OpencvModulesType) => ({ 113 | opencvModule, 114 | libPath: this.resolveLib(opencvModule), 115 | }), 116 | ); 117 | for (const m of extra) { 118 | if (m.opencvModule === "world") { 119 | continue; 120 | } 121 | if (m.libPath) { 122 | modules.push(m); 123 | } 124 | } 125 | return modules; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## V 1.1.1 4 | 5 | - detect opencv from vcpkg setup 6 | 7 | ## V 1.1.0 8 | 9 | - remove npmlog package 10 | - improve error message 11 | 12 | ## V 1.0.0 13 | 14 | - project converted to deno 15 | - the npmjs project is generated by deno dnt 16 | - @arethetypeswrong/cli find no issue in CJS and ESM exports 17 | - New Log export 18 | - New pc export (like picocolors) 19 | 20 | ## V 0.9.3 21 | 22 | - export .d.ts file twice, to avoid ESM / CJS warnings 23 | - use tsc --build instead of --project (better performance, more readable) 24 | 25 | ## V 0.9.2 26 | 27 | - improve ESM exports 28 | 29 | ## V 0.9.1 30 | 31 | - fix error in OpenCVBuildEnv.listBuild 32 | 33 | ## V 0.9.0 34 | 35 | - restore the npmjs package glob 36 | - improve MacOS Brew detection 37 | - use existing openCV by default 38 | 39 | ## V 0.8.4 40 | 41 | - npmlog can now be disabled 42 | 43 | ## V 0.8.3 44 | 45 | - 0.8.2 deployed failed 46 | 47 | ## V 0.8.2 48 | 49 | - npm-opencv-build can no longer provide a diffrent bulit version from asked 50 | version. 51 | - fix esm/cjs dual stack 52 | 53 | ## V 0.8.1 54 | 55 | - fix 0.8.0 regression 56 | 57 | ## V 0.8.0 58 | 59 | - improve openCV path autodetection for chocolatey installation 60 | 61 | ## V 0.7.8 62 | 63 | - fix ESM/commonJS dual stack 64 | - update deps verison 65 | 66 | ## V 0.7.7 67 | 68 | - will use the latest build version even if the version differ from you current 69 | env data. 70 | - update -DBUILD_LIST handling 71 | 72 | ## V 0.7.6 73 | 74 | - fix parameter separator detection by piercus 75 | 76 | ## V 0.7.5 77 | 78 | - improve autoBuildFlags support 79 | 80 | ## V 0.7.4 81 | 82 | - force world module detection 83 | 84 | ## V 0.7.3 85 | 86 | - change default build module list, remove world module 87 | - add a symlink named latest pointing to the latest build 88 | 89 | ## V 0.7.2 90 | 91 | - restore previous compiled module list from v 0.6.3 92 | 93 | ## V 0.7.1 94 | 95 | - world module do not exclude other modules in auto-build.json (fix img_hash 96 | modules) 97 | 98 | ## V 0.7.0 99 | 100 | - Rollback ESM to CJS first (ESM build is still available) 101 | - modules in cMakeflags are now sort in alphabetic order to keep build hash 102 | consistante. 103 | - build module list is now an up to date set 104 | - img_hash is now enabled by default 105 | - all cuda module (except cudalegacy) are now enabled once cuda is enabled 106 | - auto-build.json now contains list of enabled modules. 107 | 108 | ## V 0.6.3 109 | 110 | - CJS First 111 | - enforce opencv version check durring version detection 112 | 113 | ## V 0.6.2 114 | 115 | - CJS + ESM build 116 | 117 | ## V 0.6.1 118 | 119 | - improve OpenCVBuildEnv, exposing new static methods 120 | 121 | ## V 0.6.0 122 | 123 | - Improve cuda builds 124 | - add --cudaArch 125 | - generate build-cmd.bat/sh in build directory 126 | - more log 127 | - fix error logs 128 | - force keep previous hash build once loaded. 129 | - fix V 0.5.13 regression on boolean flags. 130 | 131 | ## V 0.5.13 132 | 133 | - improve configuration boolean flag parsing, "0", "false", "off", "disable" and 134 | "disabled" are mean false 135 | 136 | ## V 0.5.12 137 | 138 | - less tiny-glob usage 139 | 140 | ## V 0.5.11 141 | 142 | - improve OpenCVBuildEnv.autoLocatePrebuild() add verbose output 143 | 144 | ## V 0.5.10 145 | 146 | - add public static OpenCVBuildEnv.autoLocatePrebuild() 147 | 148 | ## V 0.5.9 149 | 150 | - findMSBuild now return all MsBuild version 151 | - force resolve absolute path for msbuild binary 152 | - improve logs 153 | 154 | ## V 0.5.8 155 | 156 | - bump @u4/tiny-glob to ignore walking on non existing directories. 157 | 158 | ## V 0.5.7 159 | 160 | - fork tiny-glob to fix windows build support. 161 | [issue47](https://github.com/UrielCh/opencv4nodejs/issues/47) 162 | - accept PR3 (fix for when /opt/homebrew/Cellar/opencv does not exist) 163 | - accept PR4 (Update script do-install bin\main.js in package.json) 164 | 165 | ## V 0.5.6 166 | 167 | - fix path interversion lib <-> include for Linux and MacOs 168 | 169 | ## V 0.5.5 170 | 171 | - fix tiny-glob usage. 172 | 173 | ## V 0.5.4 174 | 175 | - add autodetection mode for chocolatey on windows, brew on macos, and 176 | debian-base distrib 177 | - remove glob in favor of tiny-glob 10 times smaller 178 | 179 | ## V 0.5.3 180 | 181 | - fix prebuild openCV usage. 182 | 183 | ## V 0.5.2 184 | 185 | - fix regression in agument parsing bis 186 | 187 | ## V 0.5.1 188 | 189 | - fix regression in agument parsing 190 | - fix cuda detection code 191 | 192 | ## V 0.5.0 193 | 194 | - fix -h alias from --help 195 | - remove build log at runtime. 196 | - re-write cmake parameter handle 197 | - disable buy default some opencv modules 198 | - add -DBUILD_ZLIB=OFF by default, fix MacOs X build error 199 | - default openCV version build is now 4.5.5 200 | - OpenCVBuilder.env can be modifyed immediately after his instanciation to 201 | enable/disable openCV modules. 202 | - add --git-cache or OPENCV_GIT_CACHE env variabe cache git data on local disk 203 | 204 | ## V 0.4.7 205 | 206 | - add --dry-run parameter to debug openCV build failure. 207 | - do not build module wechat_qrcode 208 | - improve output text readability 209 | 210 | ## V 0.4.6 211 | 212 | - add static methods in OpenCVBuildEnv. 213 | 214 | ## V 0.4.5 215 | 216 | - fix OPENCV4NODEJS_DISABLE_AUTOBUILD 217 | 218 | ## V 0.4.4 219 | 220 | - fix disable autobuid 221 | 222 | ## V 0.4.3 223 | 224 | - allow usage of all Microsoft buildtools editions. 225 | [issue 7](https://github.com/UrielCh/opencv4nodejs/issues/7) 226 | - dump dependencies versions 227 | 228 | ## V 0.4.2 229 | 230 | - Change Typescript build target to ES2017 so it works with electron 231 | 232 | ## V 0.4.1 233 | 234 | - drop old findVs2017 script 235 | - fix findMSBuild (was locked to vs 2019) 236 | - add WARNING if multiple MSBuild are available 237 | 238 | ## V 0.4.0 239 | 240 | - Main release 241 | -------------------------------------------------------------------------------- /_build_npm.ts: -------------------------------------------------------------------------------- 1 | // dnt deps can not be moved to dev_deps.ts 2 | import { build, emptyDir, type PackageJson } from "@deno/dnt"; 3 | import { pc } from "./deps.ts"; 4 | 5 | // deno run -A _build_npm.ts 1.0.0 6 | // cd npm 7 | // ncu -i 8 | export async function buildDnt() { 9 | let version = Deno.args[0]; 10 | const GITHUB_REF = Deno.env.get("GITHUB_REF"); 11 | const PKG_VERSION = Deno.env.get("PKG_VERSION"); 12 | 13 | if (!version) { 14 | if (PKG_VERSION) { 15 | console.log(`NPM_VERSION values is "${pc.green(PKG_VERSION)}"`); 16 | version = PKG_VERSION; 17 | } else if (GITHUB_REF) { 18 | // drop the ref/tag/ and the v prefix 19 | version = GITHUB_REF.replace(/^.+\/[vV]?/g, ""); 20 | console.log( 21 | `GITHUB_REF values is ${ 22 | pc.green( 23 | GITHUB_REF, 24 | ) 25 | } will be used as version: "${pc.green(version)}"`, 26 | ); 27 | } 28 | } 29 | 30 | if (!version) { 31 | console.error("Missing version number"); 32 | console.error("usage: deno run -A _build_npm.ts 0.0.0"); 33 | Deno.exit(-1); 34 | } 35 | // allow only semver string 36 | if (!version.match(/[\d]+\.[\d]+\.[\d]+/)) { 37 | console.error( 38 | `version number ${ 39 | pc.green( 40 | version, 41 | ) 42 | } do not match Semantic Versioning syntax ${ 43 | pc.green( 44 | "major.minor.path", 45 | ) 46 | }`, 47 | ); 48 | Deno.exit(-1); 49 | } 50 | const prj = "npm-opencv-build"; 51 | const packageJson: PackageJson = { 52 | // package.json properties 53 | name: "@u4/opencv-build", 54 | author: "Uriel Chemouni (https://uriel.deno.dev/)", 55 | license: "MIT", 56 | funding: `https://github.com/UrielCh/${prj}?sponsor=1`, 57 | contributors: [ 58 | "Uriel Chemouni (https://uriel.ovh/)", 59 | "justadudewhohacks (https://github.com/justadudewhohacks)", 60 | ], 61 | description: 62 | "Script to auto build recent OpenCV + contrib via npm 2024 Edition", 63 | keywords: [ 64 | "opencv", 65 | "build", 66 | "opencv4nodejs", 67 | ], 68 | homepage: `https://github.com/UrielCh/${prj}`, 69 | version, 70 | repository: { 71 | type: "git", 72 | url: `git+https://github.com/UrielCh/${prj}.git`, 73 | }, 74 | bugs: { 75 | url: `https://github.com/UrielCh/${prj}/issues`, 76 | }, 77 | bin: { 78 | // "opencv-build-npm": "script/main.js", 79 | "opencv-build-npm": "esm/main.js", // esm looks to works fine with modern nodejs 80 | }, 81 | scripts: { 82 | "checkExports": "npx @arethetypeswrong/cli $(npm pack)", 83 | "install_macm1": 84 | 'node script/main.js --version 4.10.0 --flag="-DCMAKE_SYSTEM_PROCESSOR=arm64 -DCMAKE_OSX_ARCHITECTURES=arm64"', 85 | "install_4_9_0_cuda_30XX": 86 | "npm run build && cross-env OPENCV4NODEJS_DISABLE_AUTOBUILD= node bin/main.js --keepsource --version 4.10.0 --cuda --cudaArch=8.6", 87 | }, 88 | "engine-strict": { 89 | node: ">=18", 90 | }, 91 | dependencies: { 92 | "@denodnt/logger": "^1.1.6", 93 | // "picocolors": "1.0.0", 94 | }, 95 | //devDependencies: { 96 | // "@types/npmlog": "^7.0.0", 97 | //}, 98 | }; 99 | await emptyDir("./npm"); 100 | 101 | await build({ 102 | entryPoints: [ 103 | { 104 | kind: "export", 105 | name: ".", 106 | path: "mod.ts", 107 | }, 108 | { 109 | kind: "bin", 110 | name: "opencv-build-npm", 111 | path: "main.ts", 112 | // path: "script/main.js", 113 | }, 114 | ], 115 | outDir: "./npm", 116 | test: true, 117 | // declaration: "separate", 118 | shims: { 119 | // see JS docs for overview and more options 120 | deno: true, 121 | // webSocket: true, 122 | // undici: true, 123 | custom: [ 124 | // { 125 | // package: { 126 | // name: "stream/web", 127 | // }, 128 | // globalNames: ["ReadableStream", "TransformStream"], 129 | // }, 130 | // { 131 | // globalNames: [{ name: "MessageEvent", typeOnly: true }], 132 | // package: { 133 | // name: "ws", 134 | // }, 135 | // } 136 | ], 137 | }, 138 | compilerOptions: { 139 | lib: ["ESNext"], 140 | }, 141 | package: packageJson, 142 | mappings: { 143 | // "jsr:@std/fmt/colors": 144 | // "https://deno.land/std@0.223.0/fmt/colors.ts": { 145 | // name: "picocolors", 146 | // version: "1.0.0", 147 | // peerDependency: false, 148 | // }, 149 | // "jsr:@deno-lib/logger@^1.1.6": { 150 | // name: "@denodnt/logger", 151 | // version: "1.1.6", 152 | // peerDependency: false, 153 | // }, 154 | }, 155 | }); 156 | 157 | // post build steps 158 | console.log("extra build steps"); 159 | console.log("cwd:", Deno.cwd()); 160 | Deno.copyFileSync("CHANGELOG.md", "npm/CHANGELOG"); 161 | // Deno.copyFileSync("LICENSE", "npm/LICENSE"); 162 | let readme = ""; 163 | readme = Deno.readTextFileSync("README.md"); 164 | // readme = readme.replaceAll( 165 | // "https://deno.land/x/midjourney_discord_api/mod.ts", 166 | // "midjourney-discord-api", 167 | // ); 168 | Deno.writeTextFileSync("npm/README.md", readme); 169 | // Deno.copyFileSync("README.md", "npm/README.md"); 170 | // Deno.mkdirSync("npm/types/types"); 171 | // const files = Deno.readDirSync("types"); 172 | // for (const file of files) { 173 | // if (!file.isFile) 174 | // continue; 175 | // let text = Deno.readTextFileSync(`types/${file.name}`) 176 | // text = text.replace(/.d.ts(["'])/g, "$1"); 177 | // Deno.writeTextFileSync(`npm/types/types/${file.name}`, text); 178 | // console.log(`copy types/${file.name} to npm/types/types/${file.name}`) 179 | // } 180 | //Deno.copyFileSync("types", "npm/types"); 181 | } 182 | 183 | if (import.meta.main) { 184 | buildDnt(); 185 | } 186 | -------------------------------------------------------------------------------- /src/OpenCVBuilder_test.ts: -------------------------------------------------------------------------------- 1 | //import { type OpenCVBuildEnvParams } from '../dist/types/index' 2 | // import { type OpenCVBuildEnvParams } from '../src/index.ts' 3 | // import { OpenCVBuildEnv, OpenCVBuilder } from '../dist/cjs/index' 4 | import OpenCVBuildEnv from "./OpenCVBuildEnv.ts"; 5 | import { OpenCVBuilder } from "./OpenCVBuilder.ts"; 6 | import { assert } from "../dev_deps.ts"; 7 | 8 | // chai.use(require('chai-string')); 9 | 10 | // export class FakeOpenCVBuildEnv extends OpenCVBuildEnv { 11 | // constructor(opts: OpenCVBuildEnvParams) { 12 | // super(opts); 13 | // } 14 | // 15 | // public setPlatform(platform: NodeJS.Platform) { 16 | // super.setPlatform(platform); 17 | // } 18 | // } 19 | 20 | Deno.test("testLib", function testLib(t) { 21 | // console.log('testLib started'); 22 | // console.log('testLib started'); 23 | const env = new OpenCVBuildEnv({ prebuild: "latestBuild" }); 24 | 25 | const builder = new OpenCVBuilder(env); 26 | builder.getLibs.syncPath = false; 27 | // const opencvModules = builder.env.enabledModules; 28 | assert(builder, "builder ready"); 29 | //t.step('should find .lib (win) Fullpath test', () => { 30 | // const libFiles = [ 'opencv_340.lib' ] 31 | // Platfrm.changeOS('windows'); 32 | // //env.setPlatform('win32') 33 | // builder.getLibs.libFiles = libFiles; 34 | // const res = builder.getLibs.getLibs(); 35 | // assertInstanceOf(res, Array); 36 | // assertEquals(res.length, 1); 37 | // //.to.be.an('array').lengthOf(1, `array: ${JSON.stringify(res)}`) 38 | // // expect(res[0].opencvModule).to.equal('', `opencvModule is ${res[0].opencvModule}`) 39 | // // expect(res[0].libPath).to.equal(path.join(env.opencvLibDir, libFiles[0])) 40 | // }) 41 | 42 | //t.step('should find .so (unix)', () => { 43 | // const libFiles = [ 'libopencv_.so' ] 44 | // Platfrm.changeOS('linux') 45 | // builder.getLibs.libFiles = libFiles; 46 | // const res = builder.getLibs.getLibs(); 47 | // assertInstanceOf(res, Array); 48 | // assertEquals(res.length, 1); 49 | // assertEquals(res[0].opencvModule, '') 50 | // //assert(res[0].libPath.endWith(libFiles[0])); 51 | //}) 52 | /* 53 | // 54 | // it('should find .dylib (osX)', () => { 55 | // const libFiles = [ 'libopencv_.dylib' ] 56 | // env.setPlatform('darwin') 57 | // builder.getLibs.libFiles = libFiles; 58 | // const res = builder.getLibs.getLibs(); 59 | // expect(res).to.be.an('array').lengthOf(1) 60 | // expect(res[0].opencvModule).to.equal('') 61 | // expect(res[0].libPath).to.endWith(libFiles[0]) 62 | // }) 63 | 64 | // it('should find opencv .lib files', () => { 65 | // const coreLibFile = 'opencv_core340.lib' 66 | // const libFiles = [ 67 | // coreLibFile, 68 | // ] 69 | // //const getLibs = createFake(libFiles, { isWin: true }) 70 | // env.setPlatform('win32') 71 | // builder.getLibs.libFiles = libFiles; 72 | // const res = builder.getLibs.getLibs() 73 | // expect(res, `opencvModules should be an array of len ${opencvModules.length}`).to.be.an('array').lengthOf(opencvModules.length) 74 | // expect(res.some(({ opencvModule }) => opencvModule === 'core')) 75 | // expect(res.find(l => l.opencvModule === 'core')).to.have.property('libPath').to.endWith(coreLibFile) 76 | // }) 77 | 78 | // it('should find opencv .so files', () => { 79 | // const coreLibFile = 'libopencv_core.so' 80 | // const libFiles = [ 81 | // coreLibFile, 82 | // ] 83 | // env.setPlatform('linux') 84 | // builder.getLibs.libFiles = libFiles; 85 | // const res = builder.getLibs.getLibs() 86 | // expect(res).to.be.an('array').lengthOf(opencvModules.length) 87 | // expect(res.some(({ opencvModule }) => opencvModule === 'core')) 88 | // expect(res.find(l => l.opencvModule === 'core')).to.have.property('libPath').to.endWith(coreLibFile) 89 | // }) 90 | 91 | // it('should find opencv .dylib files', () => { 92 | // const coreLibFile = 'libopencv_core.dylib' 93 | // const libFiles = [ 94 | // coreLibFile, 95 | // ] 96 | // env.setPlatform('darwin') 97 | // builder.getLibs.libFiles = libFiles; 98 | // const res = builder.getLibs.getLibs() 99 | // expect(res).to.be.an('array').lengthOf(opencvModules.length) 100 | // expect(res.some(({ opencvModule }) => opencvModule === 'core')) 101 | // expect(res.find(l => l.opencvModule === 'core')).to.have.property('libPath').to.endWith(coreLibFile) 102 | // }) 103 | 104 | // it('should only link .lib files with exact name match', () => { 105 | // const objdetectLibFile = 'opencv_objdetect340.lib' 106 | // const dnnObjdetectLibFile = 'opencv_dnn_objdetect340.lib' 107 | // const libFiles = [ 108 | // objdetectLibFile, 109 | // dnnObjdetectLibFile, 110 | // ] 111 | // env.setPlatform('win32') 112 | // builder.getLibs.libFiles = libFiles; 113 | // const res = builder.getLibs.getLibs() 114 | // expect(res).to.be.an('array').lengthOf(opencvModules.length) 115 | // expect(res.some(({ opencvModule }) => opencvModule === 'objdetect')) 116 | // expect(res.find(l => l.opencvModule === 'objdetect')).to.have.property('libPath').to.endWith(objdetectLibFile) 117 | // expect(res.some(({ libPath }) => libPath === dnnObjdetectLibFile)).to.be.false 118 | // }) 119 | 120 | //it('should only link .so files with exact name match', () => { 121 | // const objdetectLibFile = 'libopencv_objdetect.so' 122 | // const dnnObjdetectLibFile = 'libopencv_dnn_objdetect.so' 123 | // const libFiles = [ 124 | // objdetectLibFile, 125 | // dnnObjdetectLibFile, 126 | // ] 127 | // env.setPlatform('linux') 128 | // builder.getLibs.libFiles = libFiles; 129 | // const res = builder.getLibs.getLibs() 130 | // expect(res).to.be.an('array').lengthOf(opencvModules.length) 131 | // expect(res.some(({ opencvModule }) => opencvModule === 'objdetect')) 132 | // expect(res.find(l => l.opencvModule === 'objdetect')).to.have.property('libPath').to.endWith(objdetectLibFile) 133 | // expect(res.some(({ libPath }) => libPath === dnnObjdetectLibFile)).to.be.false 134 | //}) 135 | // 136 | //it('should only link .dylib files with exact name match', () => { 137 | // const objdetectLibFile = 'libopencv_objdetect.dylib' 138 | // const dnnObjdetectLibFile = 'libopencv_dnn_objdetect.dylib' 139 | // const libFiles = [ 140 | // objdetectLibFile, 141 | // dnnObjdetectLibFile, 142 | // ] 143 | // env.setPlatform('darwin') 144 | // builder.getLibs.libFiles = libFiles; 145 | // const res = builder.getLibs.getLibs() 146 | // expect(res).to.be.an('array').lengthOf(opencvModules.length) 147 | // expect(res.some(({ opencvModule }) => opencvModule === 'objdetect')) 148 | // expect(res.find(l => l.opencvModule === 'objdetect')).to.have.property('libPath').to.endWith(objdetectLibFile) 149 | // expect(res.some(({ libPath }) => libPath === dnnObjdetectLibFile)).to.be.false 150 | //}) 151 | */ 152 | }); 153 | -------------------------------------------------------------------------------- /src/helper/detect.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import { globSync } from "npm:glob"; 3 | import { highlight } from "../utils.ts"; 4 | import { Platfrm, setEnv } from "../env.ts"; 5 | 6 | export const summery = new Set(); 7 | 8 | export function applyDetect(): void { 9 | const { OPENCV_BIN_DIR, OPENCV_LIB_DIR, OPENCV_INCLUDE_DIR } = detect(); 10 | setEnv("OPENCV_BIN_DIR", OPENCV_BIN_DIR); 11 | setEnv("OPENCV_LIB_DIR", OPENCV_LIB_DIR); 12 | setEnv("OPENCV_INCLUDE_DIR", OPENCV_INCLUDE_DIR); 13 | } 14 | 15 | export function detect(): { 16 | OPENCV_BIN_DIR: string; 17 | OPENCV_LIB_DIR: string; 18 | OPENCV_INCLUDE_DIR: string; 19 | } { 20 | const OPENCV_BIN_DIR = detectBinDir(); 21 | const OPENCV_LIB_DIR = detectLibDir(); 22 | const OPENCV_INCLUDE_DIR = detectIncludeDir(); 23 | return { OPENCV_BIN_DIR, OPENCV_LIB_DIR, OPENCV_INCLUDE_DIR }; 24 | } 25 | 26 | // detect_OPENCV_BIN_DIR 27 | // detect_OPENCV_LIB_DIR 28 | // detect_OPENCV_INCLUDE_DIR 29 | 30 | export function detectBinDir(): string { 31 | // chocolatey 32 | if (Platfrm.isWindows) { 33 | const lookups = [ 34 | // chocolatey 35 | "c:/tools/opencv/build/x64/vc*/bin", 36 | // vcpkg 37 | "c:/vcpkg/packages/opencv4_x64-windows/bin", 38 | ]; 39 | // const candidates = ["c:\\tools\\opencv\\build\\x64\\vc14\\bin", "c:\\tools\\opencv\\build\\x64\\vc16\\bin"]; 40 | let candidates: string[] = []; 41 | for (const lookup of lookups) { 42 | candidates = [...candidates, ...globSync(lookup)]; 43 | } 44 | let fnd = false; 45 | for (const candidate of candidates) { 46 | if (fs.existsSync(candidate)) { 47 | fnd = true; 48 | summery.add(`OPENCV_BIN_DIR resolved as ${highlight(candidate)}`); 49 | return candidate; 50 | } 51 | } 52 | if (!fnd) { 53 | summery.add( 54 | `failed to resolve OPENCV_BIN_DIR from ${lookups.join(", ")} => ${candidates.join( 55 | ",", 56 | )}`, 57 | ); 58 | } 59 | } else if (Platfrm.isLinux) { 60 | const candidate = "/usr/bin/"; 61 | if (fs.existsSync(candidate)) { 62 | summery.add("OPENCV_BIN_DIR resolved"); 63 | return candidate; 64 | } else { 65 | summery.add(`failed to resolve OPENCV_BIN_DIR from ${candidate}`); 66 | } 67 | } else if (Platfrm.isMac) { 68 | const lookups = [ 69 | "/opt/homebrew/Cellar/opencv/*/bin", 70 | "/usr/local/Cellar/opencv/*/bin", 71 | ]; 72 | const candidates = [...globSync(lookups[0]), ...globSync(lookups[1])]; 73 | if (candidates.length > 1) { 74 | summery.add( 75 | `homebrew detection found more than one openCV in ${lookups.join(",")}`, 76 | ); 77 | } 78 | if (candidates.length) { 79 | const candidate = candidates[0]; 80 | summery.add(`OPENCV_BIN_DIR resolved as ${candidate}`); 81 | return candidate; 82 | } 83 | summery.add(`failed to resolve OPENCV_BIN_DIR from ${lookups.join(",")}`); 84 | } 85 | return ""; 86 | } 87 | 88 | export function detectLibDir(): string { 89 | if (Platfrm.isWindows) { 90 | const lookups = [ 91 | // chocolatey 92 | "c:/tools/opencv/build/x64/vc*/lib", 93 | // vcpkg 94 | "c:/vcpkg/packages/opencv4_x64-windows/lib", 95 | ]; 96 | // const candidates = ["c:\\tools\\opencv\\build\\x64\\vc14\\bin", "c:\\tools\\opencv\\build\\x64\\vc16\\bin"]; 97 | let candidates: string[] = []; 98 | for (const lookup of lookups) { 99 | candidates = [...candidates, ...globSync(lookup)]; 100 | } 101 | 102 | let fnd = false; 103 | for (const candidate of candidates) { 104 | if (fs.existsSync(candidate)) { 105 | fnd = true; 106 | summery.add(`OPENCV_LIB_DIR resolved as ${highlight(candidate)}`); 107 | return candidate; 108 | } 109 | } 110 | if (!fnd) { 111 | summery.add( 112 | `failed to resolve OPENCV_LIB_DIR from ${lookups.join(", ")} => ${candidates.join( 113 | ",", 114 | )}`, 115 | ); 116 | } 117 | } else if (Platfrm.isLinux) { 118 | const lookup = "/usr/lib/*-linux-gnu"; 119 | // tiny-blob need to be fix bypassing th issue 120 | //const [candidate] = fs.readdirSync('/usr/lib/').filter((a: string) => a.endsWith('-linux-gnu')).map(a => `/usr/lib/${a}`); 121 | const [candidate] = globSync(lookup); 122 | if (candidate) { 123 | summery.add(`OPENCV_LIB_DIR resolved as ${candidate}`); 124 | return candidate; 125 | } else { 126 | summery.add(`failed to resolve OPENCV_LIB_DIR from ${lookup}`); 127 | } 128 | } else if (Platfrm.isMac) { 129 | const lookups = [ 130 | "/opt/homebrew/Cellar/opencv/*/lib", 131 | "/usr/local/Cellar/opencv/*/lib", 132 | ]; 133 | const candidates = [...globSync(lookups[0]), ...globSync(lookups[1])]; 134 | if (candidates.length > 1) { 135 | summery.add( 136 | `homebrew detection found more than one openCV in ${lookups.join(",")}`, 137 | ); 138 | } 139 | if (candidates.length) { 140 | const candidate = candidates[0]; 141 | summery.add(`OPENCV_LIB_DIR resolved as ${candidate}`); 142 | return candidate; 143 | } else { 144 | summery.add(`failed to resolve OPENCV_BIN_DIR from ${lookups.join(",")}`); 145 | } 146 | } 147 | return ""; 148 | } 149 | 150 | /** 151 | * detect OPENCV_INCLUDE_DIR 152 | */ 153 | export function detectIncludeDir(): string { 154 | if (Platfrm.isWindows) { 155 | const candidates = [ 156 | // chocolatey 157 | "c:\\tools\\opencv\\build\\include", 158 | // vcpkg 159 | "c:\\vcpkg\\packages\\opencv4_x64-windows\\include", 160 | ]; 161 | 162 | for (const candidate of candidates) { 163 | if (fs.existsSync(candidate)) { 164 | summery.add("OPENCV_INCLUDE_DIR resolved"); 165 | return candidate; 166 | } 167 | } 168 | summery.add( 169 | `failed to resolve OPENCV_INCLUDE_DIR from ${candidates.join(", ")}`, 170 | ); 171 | } else if (Platfrm.isLinux) { 172 | const candidate = "/usr/include/opencv4/"; 173 | if (fs.existsSync(candidate)) { 174 | summery.add(`OPENCV_INCLUDE_DIR resolved as ${candidate}`); 175 | return candidate; 176 | } else { 177 | summery.add(`failed to resolve OPENCV_INCLUDE_DIR from ${candidate}`); 178 | } 179 | } else if (Platfrm.isMac) { 180 | const lookups = [ 181 | "/opt/homebrew/Cellar/opencv/*/include", 182 | "/usr/local/Cellar/opencv/*/include", 183 | ]; 184 | const candidates = [...globSync(lookups[0]), ...globSync(lookups[1])]; 185 | if (candidates.length > 1) { 186 | summery.add( 187 | `homebrew detection found more than one openCV in ${lookups.join(",")}`, 188 | ); 189 | } 190 | if (candidates.length) { 191 | const candidate = candidates[0]; 192 | summery.add(`OPENCV_INCLUDE_DIR resolved as ${candidate}`); 193 | return candidate; 194 | } else { 195 | summery.add( 196 | `failed to resolve OPENCV_INCLUDE_DIR from ${lookups.join(",")}`, 197 | ); 198 | } 199 | } 200 | return ""; 201 | } 202 | -------------------------------------------------------------------------------- /src/OpenCVBuilder.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import * as utils from "./utils.ts"; 3 | import type { AutoBuildFile } from "./types.ts"; 4 | import { getLibsFactory } from "./getLibsFactory.ts"; 5 | import { SetupOpencv } from "./setupOpencv.ts"; 6 | import { Constant } from "./constants.ts"; 7 | import OpenCVBuildEnv from "./OpenCVBuildEnv.ts"; 8 | import { 9 | args2Option, 10 | genHelp, 11 | OPENCV_PATHS_ENV, 12 | OpenCVBuildEnvParams, 13 | } from "./misc.ts"; 14 | import Log, { logger } from "./Log.ts"; 15 | 16 | export class OpenCVBuilder { 17 | public readonly constant: Constant; 18 | public readonly getLibs: getLibsFactory; 19 | public readonly env: OpenCVBuildEnv; 20 | 21 | constructor(opts?: OpenCVBuildEnv | OpenCVBuildEnvParams | string[]) { 22 | if (Array.isArray(opts)) { 23 | opts = args2Option(opts); 24 | if (opts.verbose) { 25 | logger.enableConsole(); 26 | // Log.level = "verbose"; 27 | } 28 | if (opts.extra && (opts.extra.help || opts.extra.h)) { 29 | console.log("npm-opencv-build usage:"); 30 | console.log(genHelp()); 31 | Deno.exit(1); 32 | // process.exit(1); 33 | } 34 | } 35 | if (opts instanceof OpenCVBuildEnv) { 36 | this.env = opts; 37 | } else { 38 | this.env = new OpenCVBuildEnv(opts); 39 | } 40 | if (!this.env.prebuild) { 41 | Log.log( 42 | "info", 43 | "init", 44 | `${utils.highlight("Workdir")} will be: ${utils.formatNumber("%s")}`, 45 | this.env.opencvRoot, 46 | ); 47 | } 48 | this.constant = new Constant(this); 49 | this.getLibs = new getLibsFactory(this); 50 | } 51 | 52 | private checkInstalledLibs(autoBuildFile: AutoBuildFile): boolean { 53 | let hasLibs = true; 54 | 55 | Log.log("info", "install", "checking for opencv libraries"); 56 | 57 | if (!fs.existsSync(this.env.opencvLibDir)) { 58 | Log.log( 59 | "info", 60 | "install", 61 | "library dir does not exist:", 62 | this.env.opencvLibDir, 63 | ); 64 | return false; 65 | } 66 | const installedLibs = this.getLibs.getLibs(); 67 | 68 | autoBuildFile.modules.forEach(({ opencvModule, libPath }) => { 69 | if (!libPath) { 70 | Log.log( 71 | "info", 72 | "install", 73 | "%s: %s", 74 | opencvModule, 75 | "ignored", 76 | ); 77 | return; 78 | } 79 | const foundLib = installedLibs.find((lib) => 80 | lib.opencvModule === opencvModule 81 | ); 82 | hasLibs = hasLibs && !!foundLib; 83 | Log.log( 84 | "info", 85 | "install", 86 | `lib ${utils.formatNumber("%s")}: ${utils.light("%s")}`, 87 | opencvModule, 88 | foundLib ? foundLib.libPath : "not found", 89 | ); 90 | }); 91 | return hasLibs; 92 | } 93 | 94 | async install(): Promise { 95 | let time = Date.now(); 96 | // if project directory has a package.json containing opencv4nodejs variables 97 | // apply these variables to the process environment 98 | // this.env.applyEnvsFromPackageJson() 99 | 100 | if (this.env.isAutoBuildDisabled) { 101 | Log.log( 102 | "info", 103 | "install", 104 | `${ 105 | utils.highlight("OPENCV4NODEJS_DISABLE_AUTOBUILD") 106 | } is set skipping auto build...`, 107 | ); 108 | const setup = new SetupOpencv(this); 109 | setup.writeAutoBuildFile(true); 110 | setup.linkBuild(); 111 | return; 112 | } 113 | Log.log( 114 | "info", 115 | "install", 116 | `if you want to use an own OpenCV build set ${ 117 | utils.highlight("OPENCV4NODEJS_DISABLE_AUTOBUILD") 118 | } to 1, and fill ${ 119 | OPENCV_PATHS_ENV.map(utils.highlight).join(", ") 120 | } environement variables`, 121 | ); 122 | // prevent rebuild on every install 123 | const autoBuildFile = this.env.readAutoBuildFile(); 124 | if (autoBuildFile) { 125 | Log.log( 126 | "info", 127 | "install", 128 | `found previous build summery auto-build.json: ${ 129 | utils.highlight(this.env.autoBuildFile) 130 | }`, 131 | ); 132 | 133 | if (autoBuildFile.opencvVersion !== this.env.opencvVersion) { 134 | // can no longer occure with this version of opencv4nodejs-builder 135 | Log.log( 136 | "info", 137 | "install", 138 | `auto build opencv version is ${autoBuildFile.opencvVersion}, but AUTOBUILD_OPENCV_VERSION=${this.env.opencvVersion}, Will rebuild`, 139 | ); 140 | } else if (autoBuildFile.autoBuildFlags !== this.env.autoBuildFlags) { 141 | // should no longer occure since -MD5(autoBuildFlags) is append to build path 142 | Log.log( 143 | "info", 144 | "install", 145 | `auto build flags are ${autoBuildFile.autoBuildFlags}, but AUTOBUILD_FLAGS is ${this.env.autoBuildFlags}, Will rebuild`, 146 | ); 147 | } else { 148 | const hasLibs = this.checkInstalledLibs(autoBuildFile); 149 | if (hasLibs) { 150 | Log.log( 151 | "info", 152 | "install", 153 | `all libraries are installed in ${ 154 | utils.highlight(this.env.opencvLibDir) 155 | } => ${utils.highlight("Skip building")}`, 156 | ); 157 | return; 158 | } else { 159 | Log.log("info", "install", "missing some libraries"); 160 | } 161 | } 162 | } else { 163 | // OpenCVBuildEnv.log('info', 'install', `failed to find auto-build.json: ${this.env.autoBuildFile}`) 164 | } 165 | 166 | Log.log("info", "install", ""); 167 | Log.log("info", "install", "running install script..."); 168 | Log.log("info", "install", ""); 169 | Log.log( 170 | "info", 171 | "install", 172 | `opencv version: ${utils.formatNumber("%s")}`, 173 | this.env.opencvVersion, 174 | ); 175 | Log.log( 176 | "info", 177 | "install", 178 | `with opencv contrib: ${utils.formatNumber("%s")}`, 179 | this.env.isWithoutContrib ? "no" : "yes", 180 | ); 181 | Log.log( 182 | "info", 183 | "install", 184 | `custom build flags: ${utils.formatNumber("%s")}`, 185 | this.env.autoBuildFlags || "< none >", 186 | ); 187 | Log.log("info", "install", ""); 188 | 189 | try { 190 | await utils.requireGit(); 191 | await utils.requireCmake(); 192 | const setup = new SetupOpencv(this); 193 | await setup.start(); 194 | time = Date.now() - time; 195 | const date = new Date(time); 196 | const timeString = date.toISOString().substring(11, 19); 197 | Log.log( 198 | "info", 199 | "install", 200 | `Total Build Time: ${utils.formatNumber(timeString)}`, 201 | ); 202 | } catch (err) { 203 | if (err instanceof Error) { 204 | Log.log("error", "install", err.toString()); 205 | } else { 206 | Log.log("error", "install", JSON.stringify(err)); 207 | } 208 | Deno.exit(1); 209 | // process.exit(1); 210 | } 211 | } 212 | } 213 | 214 | export default OpenCVBuilder; 215 | -------------------------------------------------------------------------------- /src/StaticTools.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import os from "node:os"; 3 | import path from "node:path"; 4 | import { getDirname, getEnv, setEnv } from "./env.ts"; 5 | import type { OpenCVBuildEnvParams } from "./misc.ts"; 6 | import * as detector from "./helper/detect.ts"; 7 | import type { AutoBuildFile } from "./types.ts"; 8 | import Log from "./Log.ts"; 9 | import { highlight } from "./utils.ts"; 10 | 11 | export interface BuildDesc { 12 | autobuild: string; 13 | buildInfo: AutoBuildFile; 14 | dir: string; 15 | hash: string; 16 | date: Date; 17 | } 18 | 19 | class StaticTools { 20 | private readEnvsFromPackageJsonLog = 0; 21 | 22 | /** 23 | * Find the proper root dir, this directory will contains all openCV source code and a subfolder per build 24 | * @param opts 25 | * @returns 26 | */ 27 | public getBuildDir(opts = {} as OpenCVBuildEnvParams) { 28 | let buildRoot = opts.buildRoot || getEnv("OPENCV_BUILD_ROOT") || 29 | path.join(getDirname(), ".."); 30 | if (buildRoot[0] === "~") { 31 | buildRoot = path.join(os.homedir(), buildRoot.slice(1)); 32 | } 33 | return buildRoot; 34 | } 35 | 36 | public getPackageJson(): string { 37 | // return path.resolve(process.cwd(), "package.json"); 38 | return Deno.realPathSync(Deno.cwd() + "/package.json"); 39 | } 40 | 41 | /** 42 | * autodetect path using common values. 43 | * @return number of updated env variable from 0 to 3 44 | */ 45 | public autoLocatePrebuild(): { changes: number; summery: string[] } { 46 | let changes = 0; 47 | const summery = [] as string[]; 48 | if (!getEnv("OPENCV_BIN_DIR")) { 49 | const candidate = detector.detectBinDir(); 50 | if (candidate) { 51 | setEnv("OPENCV_BIN_DIR", candidate); 52 | changes++; 53 | } 54 | } 55 | if (!getEnv("OPENCV_LIB_DIR")) { 56 | const candidate = detector.detectLibDir(); 57 | if (candidate) { 58 | setEnv("OPENCV_LIB_DIR", candidate); 59 | changes++; 60 | } 61 | } 62 | if (!getEnv("OPENCV_INCLUDE_DIR")) { 63 | const candidate = detector.detectIncludeDir(); 64 | if (candidate) { 65 | setEnv("OPENCV_INCLUDE_DIR", candidate); 66 | changes++; 67 | } 68 | } 69 | return { changes, summery }; 70 | } 71 | 72 | /** 73 | * list existing build in the diven directory 74 | * @param rootDir build directory 75 | * @returns builds list 76 | */ 77 | public listBuild(rootDir: string): Array { 78 | let workFolderContent: string[] = []; 79 | try { 80 | workFolderContent = fs.readdirSync(rootDir); 81 | } catch (err) { 82 | throw new Error( 83 | "Failed to list directory: " + rootDir + 84 | " Check environement variable OPENCV_BUILD_ROOT, " + err, 85 | ); 86 | } 87 | const versions = workFolderContent 88 | .filter((n) => n.startsWith("opencv-")) 89 | .map((dir) => { 90 | const autobuild = path.join(rootDir, dir, "auto-build.json"); 91 | try { 92 | const stats = fs.statSync(autobuild); 93 | const hash = dir.replace(/^opencv-.+-/, "-"); 94 | const buildInfo = this.readAutoBuildFile( 95 | autobuild, 96 | true, 97 | ) as AutoBuildFile; 98 | return { autobuild, dir, hash, buildInfo, date: stats.mtime }; 99 | } catch (_err) { 100 | return { 101 | autobuild, 102 | dir, 103 | hash: "", 104 | buildInfo: null, 105 | date: 0, 106 | } as unknown as BuildDesc; 107 | } 108 | }) 109 | .filter((n) => n.buildInfo); 110 | return versions; 111 | } 112 | 113 | /** 114 | * Read a parse an existing autoBuildFile 115 | * @param autoBuildFile file path 116 | * @returns 117 | */ 118 | public readAutoBuildFile( 119 | autoBuildFile: string, 120 | quiet?: boolean, 121 | ): AutoBuildFile | undefined { 122 | try { 123 | const fileExists = fs.existsSync(autoBuildFile); 124 | if (fileExists) { 125 | const autoBuildFileData = JSON.parse( 126 | fs.readFileSync(autoBuildFile).toString(), 127 | ) as AutoBuildFile; 128 | if ( 129 | !autoBuildFileData.opencvVersion || 130 | !("autoBuildFlags" in autoBuildFileData) || 131 | !Array.isArray(autoBuildFileData.modules) 132 | ) { 133 | // if (quiet) return undefined; 134 | throw new Error( 135 | `auto-build.json has invalid contents, please delete the file: ${autoBuildFile}`, 136 | ); 137 | } 138 | return autoBuildFileData; 139 | } 140 | if (!quiet) { 141 | Log.log( 142 | "info", 143 | "readAutoBuildFile", 144 | "file does not exists: %s", 145 | autoBuildFile, 146 | ); 147 | } 148 | } catch (err) { 149 | //if (!quiet) 150 | if (err instanceof Error) { 151 | Log.log( 152 | "error", 153 | "readAutoBuildFile", 154 | "failed to read auto-build.json from: %s, with error: %s", 155 | autoBuildFile, 156 | err.toString(), 157 | ); 158 | } else { 159 | Log.log( 160 | "error", 161 | "readAutoBuildFile", 162 | "failed to read auto-build.json from: %s, with error: %s", 163 | autoBuildFile, 164 | JSON.stringify(err), 165 | ); 166 | } 167 | } 168 | return undefined; 169 | } 170 | 171 | /** 172 | * extract opencv4nodejs section from package.json if available 173 | */ 174 | private parsePackageJson(): { 175 | file: string; 176 | data: { opencv4nodejs?: { [key: string]: string | boolean | number } }; 177 | } | null { 178 | const absPath = this.getPackageJson(); 179 | if (!fs.existsSync(absPath)) { 180 | return null; 181 | } 182 | const data = JSON.parse(fs.readFileSync(absPath).toString()); 183 | return { file: absPath, data }; 184 | } 185 | 186 | /** 187 | * get opencv4nodejs section from package.json if available 188 | * @returns opencv4nodejs customs 189 | */ 190 | public readEnvsFromPackageJson(): { 191 | [key: string]: string | boolean | number; 192 | } | null { 193 | const rootPackageJSON = this.parsePackageJson(); 194 | if (!rootPackageJSON) { 195 | return null; 196 | } 197 | 198 | if (!rootPackageJSON.data) { 199 | if (!this.readEnvsFromPackageJsonLog++) { 200 | Log.log( 201 | "info", 202 | "config", 203 | `looking for opencv4nodejs option from ${highlight("%s")}`, 204 | rootPackageJSON.file, 205 | ); 206 | } 207 | return {}; 208 | } 209 | if (!rootPackageJSON.data.opencv4nodejs) { 210 | if (!this.readEnvsFromPackageJsonLog++) { 211 | Log.log( 212 | "info", 213 | "config", 214 | `no opencv4nodejs section found in ${highlight("%s")}`, 215 | rootPackageJSON.file, 216 | ); 217 | } 218 | return {}; 219 | } 220 | if (!this.readEnvsFromPackageJsonLog++) { 221 | Log.log( 222 | "info", 223 | "config", 224 | `found opencv4nodejs section in ${highlight("%s")}`, 225 | rootPackageJSON.file, 226 | ); 227 | } 228 | return rootPackageJSON.data.opencv4nodejs; 229 | } 230 | } 231 | 232 | const singleton = new StaticTools(); 233 | export default singleton; 234 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import child_process from "node:child_process"; 2 | import fs from "node:fs"; 3 | import { EOL } from "node:os"; 4 | import path from "node:path"; 5 | import { pc } from "../deps.ts"; 6 | import { getEnv, Platfrm } from "./env.ts"; 7 | import Log from "./Log.ts"; 8 | 9 | /** 10 | * excape spaces for shell execution 11 | * @param txt text to escape 12 | * @returns a shell no spaced parameter 13 | */ 14 | export const protect = (txt: string): string => { 15 | if (txt.includes(" ")) return `"${txt}"`; 16 | else return txt; 17 | }; 18 | 19 | export function toExecCmd(bin: string, args: string[]) { 20 | return `${protect(bin)} ${args.map(protect).join(" ")}`; 21 | } 22 | 23 | export function highlight(text: string): string { 24 | return pc.bold(pc.yellow(String(text))); 25 | } 26 | 27 | export function light(text: string): string { 28 | return pc.yellow(String(text)); 29 | } 30 | 31 | export function formatRed(text: string): string { 32 | return pc.red(String(text)); 33 | } 34 | 35 | export function formatNumber(text: string | number): string { 36 | return pc.bold(pc.green(String(text))); 37 | } 38 | 39 | export function exec( 40 | cmd: string, 41 | options?: child_process.ExecOptions, 42 | ): Promise { 43 | Log.log("silly", "install", "executing: %s", protect(cmd)); 44 | return new Promise(function (resolve, reject) { 45 | child_process.exec(cmd, options, function (err, stdout, stderr) { 46 | const _err = err || stderr; 47 | if (_err) return reject(_err); 48 | return resolve(stdout.toString()); 49 | }); 50 | }); 51 | } 52 | 53 | export function execSync( 54 | cmd: string, 55 | options?: child_process.ExecOptions, 56 | ): string { 57 | Log.log("silly", "install", "executing: %s", protect(cmd)); 58 | const stdout = child_process.execSync(cmd, options); 59 | return stdout.toString(); 60 | } 61 | 62 | /** 63 | * only used by findVs2017 64 | */ 65 | export function execFile( 66 | cmd: string, 67 | args: string[], 68 | options?: child_process.ExecOptions, 69 | ): Promise { 70 | Log.log( 71 | "silly", 72 | "install", 73 | "executing: %s %s", 74 | protect(cmd), 75 | args.map(protect).join(" "), 76 | ); 77 | return new Promise(function (resolve, reject) { 78 | const child = child_process.execFile( 79 | cmd, 80 | args, 81 | options, 82 | function (err, stdout, stderr) { 83 | const _err = err || stderr; 84 | if (_err) return reject(_err); 85 | return resolve(stdout.toString()); 86 | }, 87 | ); 88 | child.stdin && child.stdin.end(); 89 | }); 90 | } 91 | 92 | export function spawn( 93 | cmd: string, 94 | args: string[], 95 | options: child_process.ExecOptions, 96 | filters?: { 97 | err?: (data: Uint8Array) => Uint8Array | null; 98 | out?: (data: Uint8Array) => Uint8Array | null; 99 | }, 100 | ): Promise { 101 | filters = filters || {}; 102 | const filterStdout = (data: Uint8Array) => { 103 | if (filters && filters.out) { 104 | data = filters.out(data) as Uint8Array; 105 | if (!data) { 106 | return; 107 | } 108 | } 109 | // process.stdout.write(data); 110 | Deno.stdout.write(data); 111 | }; 112 | 113 | const filterStderr = (data: Uint8Array) => { 114 | if (filters && filters.err) { 115 | data = filters.err(data) as Uint8Array; 116 | if (!data) { 117 | return; 118 | } 119 | } 120 | // process.stderr.write(data); 121 | Deno.stdout.write(data); 122 | }; 123 | 124 | Log.log( 125 | "silly", 126 | "install", 127 | "spawning:", 128 | protect(cmd), 129 | args.map(protect).join(" "), 130 | ); 131 | return new Promise(function (resolve, reject) { 132 | try { 133 | const child = child_process.spawn(cmd, args, { 134 | stdio: ["inherit", "pipe", "pipe"], 135 | ...options, 136 | }); 137 | child.stderr.on("data", filterStderr); 138 | child.stdout.on("data", filterStdout); 139 | child.on("exit", function (code) { 140 | if (typeof code !== "number") { 141 | code = null; 142 | } 143 | const msg = `running: ${protect(cmd)} ${ 144 | args.map(protect).join(" ") 145 | }${EOL}in ${options 146 | .cwd as string} exited with code ${code} (for more info, set '--loglevel silly')'`; 147 | // if (code !== 0) 148 | // console.log(`End of spawn ${cmd} ${args.join(' ')} RET:`, code); 149 | if (code !== 0) { 150 | return reject(msg); 151 | } 152 | return resolve(msg); 153 | }); 154 | } catch (err) { 155 | return reject(err); 156 | } 157 | }); 158 | } 159 | 160 | async function requireCmd(cmd: string, hint: string): Promise { 161 | Log.log("silly", "install", `executing: ${pc.cyan("%s")}`, cmd); 162 | try { 163 | const stdout = await exec(cmd); 164 | Log.log("verbose", "install", `${cmd}: ${stdout.trim()}`); 165 | return stdout; 166 | } catch (err) { 167 | let errMessage = `failed to execute ${cmd}, ${hint}, error is: `; 168 | if (err instanceof Error) { 169 | errMessage += err.toString(); 170 | } else { 171 | errMessage += JSON.stringify(err); 172 | } 173 | throw new Error(errMessage); 174 | } 175 | } 176 | 177 | function requireCmdSync(cmd: string, hint: string): string { 178 | Log.log("info", "install", `executing: ${pc.cyan("%s")}`, cmd); 179 | try { 180 | const stdout = execSync(cmd); 181 | Log.log("verbose", "install", `${cmd}: ${stdout.trim()}`); 182 | return stdout; 183 | } catch (err) { 184 | let errMessage = `failed to execute ${cmd}, ${hint}, error is: `; 185 | if (err instanceof Error) { 186 | errMessage += err.toString(); 187 | } else { 188 | errMessage += JSON.stringify(err); 189 | } 190 | throw new Error(errMessage); 191 | } 192 | } 193 | 194 | export async function requireGit() { 195 | const out = await requireCmd("git --version", "git is required"); 196 | const version = out.match(/version ([\d.\w]+)/); 197 | if (version) { 198 | Log.log( 199 | "info", 200 | "install", 201 | `git Version ${formatNumber("%s")} found`, 202 | version[1], 203 | ); 204 | } 205 | } 206 | 207 | export async function requireCmake() { 208 | const out = await requireCmd( 209 | "cmake --version", 210 | "cmake is required to build opencv", 211 | ); 212 | const version = out.match(/version ([\d.\w]+)/); 213 | if (version) { 214 | Log.log( 215 | "info", 216 | "install", 217 | `cmake Version ${formatNumber("%s")} found`, 218 | version[1], 219 | ); 220 | } 221 | } 222 | 223 | let cached_cuda: null | boolean = null; 224 | /** 225 | * looks for cuda lib 226 | * @returns 227 | */ 228 | export function isCudaAvailable(): boolean { 229 | if (cached_cuda != null) { 230 | return cached_cuda; 231 | } 232 | Log.log("info", "install", "Check if CUDA is available & what version..."); 233 | if (Platfrm.isWindows) { 234 | try { 235 | requireCmdSync("nvcc --version", "CUDA availability check"); 236 | // return true; 237 | } catch (_err) { 238 | Log.log( 239 | "info", 240 | "install", 241 | "Seems like CUDA is not installed; nvcc --version call failed", 242 | ); 243 | return false; 244 | } 245 | } 246 | // Because NVCC is not installed by default & requires an extra install step, 247 | // this is work around that always works 248 | const CUDA_PATH = getEnv("CUDA_PATH"); 249 | for (const cudaPath of [CUDA_PATH, "/usr/local/cuda/"]) { 250 | if (!cudaPath) { 251 | continue; 252 | } 253 | if (!fs.existsSync(cudaPath)) { 254 | continue; 255 | } 256 | for (const file of ["version.txt", "version.json"]) { 257 | const realpath = path.resolve(cudaPath, file); 258 | if (fs.existsSync(realpath)) { 259 | const content = fs.readFileSync(realpath, "utf8"); 260 | Log.log("info", "install", content); 261 | cached_cuda = true; 262 | return true; 263 | } 264 | } 265 | } 266 | Log.log( 267 | "info", 268 | "install", 269 | `CUDA version file could not be found in {/usr/local/cuda/,CUDA_PATH}version.{txt,json}`, 270 | ); 271 | cached_cuda = false; 272 | return false; 273 | } 274 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @u4/opencv-build 2 | 3 | [![NPM Version](https://img.shields.io/npm/v/@u4/opencv-build.svg?style=flat)](https://www.npmjs.org/package/@u4/opencv-build) 4 | [![Build status](https://ci.appveyor.com/api/projects/status/uv8n2sruno95rxtq/branch/master?svg=true)](https://ci.appveyor.com/project/justadudewhohacks/npm-opencv-build/branch/master) 5 | 6 | A simple script to auto build recent OpenCV + contrib version via npm. This 7 | script is used to auto build 8 | [_opencv4nodejs_](https://github.com/UrielCh/opencv4nodejs). 9 | 10 | ## Main changes from the original project 11 | 12 | - OpenCV is explicitly build with `opencv-build-npm` and accepting parametes see 13 | `opencv-build-npm --help` 14 | - OpenCV build can now be configured with 15 | `new OpenCVBuilder({autoBuildOpencvVersion: "3.4.16", autoBuildBuildCuda: true, autoBuildWithoutContrib: false }).install()` 16 | - Each OPENCV_VERSION will be build in his own directory. 17 | - Each AUTOBUILD_FLAGS will be build in his own directory. (induce massive time 18 | gain during development) 19 | - All build can now be build outside node_modules using `OPENCV_BUILD_ROOT` 20 | environement variable. 21 | - New MSBuild.exe localisation, also works with VS 2019 22 | - Script output is now colorized. 23 | - Add some usefull log. 24 | - Big code refactor. 25 | - Enfoce typing. 26 | - Add comments and documentations. 27 | 28 | Each OpenCV build will take around 2Gb on your drive, so I recommand you to 29 | define the `OPENCV_BUILD_ROOT` environement variable to avoid duplicate 30 | buildsand avoid node_modules auto flushs. 31 | 32 | ## Install 33 | 34 | ```bash 35 | npm install opencv-build 36 | ``` 37 | 38 | ## usage 39 | 40 | ```bash 41 | npm-opencv-build usage: 42 | --version OpenCV version (OPENCV4NODEJS_AUTOBUILD_OPENCV_VERSION env variable) 43 | --flags OpenCV cMake Build flags (OPENCV4NODEJS_AUTOBUILD_FLAGS env variable) 44 | --root OpenCV-build root directory (deprecated) (INIT_CWD env variable) 45 | --buildRoot OpenCV build directory (OPENCV_BUILD_ROOT env variable) 46 | --cuda Enable cuda in OpenCV build (OPENCV4NODEJS_BUILD_CUDA env variable) 47 | --cudaArch Specify the cuda arch will drasticly reduce build time, see https://en.wikipedia.org/wiki/CUDA, 48 | ex if you have a RTX 3080 use --cudaArch=8.6, if you have also a RTX 2080 --cudaArch=7.5,8.6 49 | (OPENCV4NODEJS_BUILD_CUDA_ARCH env variable) 50 | --nocontrib Do not compile Contrib modules (OPENCV4NODEJS_AUTOBUILD_WITHOUT_CONTRIB env variable) 51 | --nobuild Do build OpenCV (OPENCV4NODEJS_DISABLE_AUTOBUILD env variable) 52 | --incDir OpenCV include directory (OPENCV_INCLUDE_DIR env variable) 53 | --libDir OpenCV library directory (OPENCV_LIB_DIR env variable) 54 | --binDir OpenCV bin directory (OPENCV_BIN_DIR env variable) 55 | --keepsources Keepsources OpenCV source after build 56 | --dry-run Display command line use to build library 57 | --git-cache Reduce Bandwide usage, by keeping a local git souce un the buildRoot (OPENCV_GIT_CACHE env variable) 58 | ``` 59 | 60 | ## Requirements 61 | 62 | - cmake 63 | 64 | ### Windows 65 | 66 | for nodejs <= 12 67 | 68 | ```bash 69 | npm install --global windows-build-tools 70 | ``` 71 | 72 | ## OpenCVBuildEnv options 73 | 74 | It's possible to specify build environment variables by passing argument to the 75 | builder script 76 | 77 | ```bash 78 | node lib/main.js --version 4.5.5 --buildRoot ~/openCV 79 | ``` 80 | 81 | with flags, do not forget the quotes `"` 82 | 83 | ```bash 84 | node lib/main.js --version 4.5.5 --buildRoot ~/openCV --flags "-DOPENCV_GENERATE_PKGCONFIG=ON -DOPENCV_PC_FILE_NAME=opencv.pc" 85 | ``` 86 | 87 | Using the bin alias 88 | 89 | ```bash 90 | opencv-build-npm --version 4.5.5 91 | ``` 92 | 93 | Or by inserting them into the `package.json` where the dependency is declared an 94 | object like: 95 | 96 | ```json 97 | { 98 | "opencv4nodejs": { 99 | "autoBuildFlags": "-DOPENCV_GENERATE_PKGCONFIG=ON -DOPENCV_PC_FILE_NAME=opencv.pc", 100 | "autoBuildOpencvVersion": "4.6.0" 101 | } 102 | } 103 | ``` 104 | 105 | By using environement varaibles. 106 | 107 | ```bash 108 | export OPENCV4NODEJS_AUTOBUILD_FLAGS="-DOPENCV_GENERATE_PKGCONFIG=ON -DOPENCV_PC_FILE_NAME=opencv.pc" 109 | export OPENCV4NODEJS_AUTOBUILD_OPENCV_VERSION="4.6.0" 110 | export OPENCV_BUILD_ROOT="~/openCV" 111 | 112 | node build/main.js 113 | ``` 114 | 115 | ### prebuild 116 | 117 | The `prebuild` is a smart version selector, to avoid futher re-compilation, 118 | accepted values are: 119 | 120 | - `"latestBuild"` use the last built version 121 | - `"latestVersion"` use the highest version number built 122 | - `"oldestBuild"` use the olderst built version 123 | - `"oldestVersion"` use the lowest version number built 124 | 125 | the `prebuild` option intend to be use at runtime, so you do not have to keep 126 | trak of the version you want to use. 127 | 128 | - this parameter can only be provide in `OpenCVBuildEnv` constructor options. 129 | - `prebuild` is ignored if OPENCV4NODEJS_DISABLE_AUTOBUILD env variable is set, 130 | or disableAutoBuild is set in package.json 131 | 132 | ### autoBuildOpencvVersion 133 | 134 | Choose the openCV version you want to build, default is 4.5.5, 135 | 136 | This option value can be provide using: 137 | 138 | - The `--version` in build script 139 | - The `autoBuildOpencvVersion` options field provided to `OpenCVBuildEnv` 140 | constructor options. 141 | - The `autoBuildOpencvVersion` field in the current package.json `opencv4nodejs` 142 | object. 143 | - The `OPENCV4NODEJS_AUTOBUILD_OPENCV_VERSION` environement variable. 144 | 145 | ### buildRoot 146 | 147 | The `buildRoot` is the most important parameter, it define the directory used to 148 | build openCV, Default value is the npm-opencv-build directory. You generaly want 149 | to use this value to persist your build files out of your `node_modules` 150 | directory, and buy doing som share openCV built between your project. 151 | 152 | This option value can be provide using: 153 | 154 | - The `--buildRoot` in build script 155 | - The `buildRoot` options field provided to `OpenCVBuildEnv` constructor 156 | options. 157 | - The `OPENCV_BUILD_ROOT` environement variable. 158 | 159 | ### git-cache 160 | 161 | The `git-cache` reduce git clone data transfert, git data will be cache in you 162 | `buildRoot`, so you will onlyt downdload all git file once. 163 | 164 | This option value can be provide using: 165 | 166 | - The `--git-cache` in build script 167 | - The `git-cache` options field provided to `OpenCVBuildEnv` constructor 168 | options. 169 | - The `OPENCV_GIT_CACHE` environement variable. 170 | 171 | ### autoBuildBuildCuda 172 | 173 | Set any value to enable, the following cMake flag will be added: 174 | 175 | - DWITH_CUDA=ON 176 | - DBUILD_opencv_cudacodec=OFF // video codec (NVCUVID) is deprecated in cuda 10, 177 | so don't add it 178 | - DCUDA_FAST_MATH=ON // optional 179 | - DWITH_CUBLAS=ON // optional 180 | 181 | This option value can be enable using: 182 | 183 | - The `--cuda` in build script 184 | - The `autoBuildBuildCuda` options field provided to `OpenCVBuildEnv` 185 | constructor options. 186 | - The `autoBuildBuildCuda` field in the current package.json `opencv4nodejs` 187 | object. 188 | - The `OPENCV4NODEJS_BUILD_CUDA` environement variable. 189 | 190 | ### autoBuildFlags 191 | 192 | Append option to CMake flags. 193 | 194 | This option value can be enable using: 195 | 196 | - The `--flags` in build script 197 | - The `autoBuildFlags` options field provided to `OpenCVBuildEnv` constructor 198 | options. 199 | - The `autoBuildFlags` field in the current package.json `opencv4nodejs` object. 200 | - The `OPENCV4NODEJS_AUTOBUILD_FLAGS` environement variable. 201 | 202 | ### autoBuildWithoutContrib 203 | 204 | Set any value to enable, this option will skip openCV Contribs. 205 | 206 | This option value can be enable using: 207 | 208 | - The `--nocontrib` in build script 209 | - The `autoBuildWithoutContrib` options field provided to `OpenCVBuildEnv` 210 | constructor options. 211 | - The `autoBuildWithoutContrib` field in the current package.json 212 | `opencv4nodejs` object. 213 | - The `OPENCV4NODEJS_AUTOBUILD_WITHOUT_CONTRIB` environement variable. 214 | 215 | ### disableAutoBuild 216 | 217 | Set any value to disable compilation from sources. 218 | 219 | This option value can be enable using: 220 | 221 | - The `--nobuild` in build script 222 | - The `disableAutoBuild` options field provided to `OpenCVBuildEnv` constructor 223 | options. 224 | - The `disableAutoBuild` field in the current package.json `opencv4nodejs` 225 | object. 226 | - The `OPENCV4NODEJS_DISABLE_AUTOBUILD` environement variable. 227 | 228 | Generaly you should prefer using the environment variables 229 | _OPENCV4NODEJS_DISABLE_AUTOBUILD_ 230 | 231 | ### opencvIncludeDir 232 | 233 | Over write the _OPENCV_INCLUDE_DIR_ environment variables 234 | 235 | ### opencvLibDir 236 | 237 | Over write the _OPENCV_LIB_DIR_ environment variables 238 | 239 | ### opencvBinDir 240 | 241 | Over write the _OPENCV_BIN_DIR_ environment variables 242 | 243 | ## build test 244 | 245 | ```bash 246 | opencv-build-npm --flags="-DBUILD_LIST=core,imgproc,imgcodecs,videoio,highgui,video,calib3d,features2d,objdetect,dnn,ml,flann,photo,stitching,gapi" --version=3.4.15 --nocontrib 247 | ``` 248 | 249 | ```bash 250 | opencv-build-npm --version=4.5.5 --nocontrib 251 | ``` 252 | -------------------------------------------------------------------------------- /src/setupOpencv.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import { EOL } from "node:os"; 3 | import path from "node:path"; 4 | import Log from "./Log.ts"; 5 | import { rimraf } from "npm:rimraf"; 6 | import type { OpenCVBuilder } from "./OpenCVBuilder.ts"; 7 | import { findMSBuild, PathVersion } from "./findMsBuild.ts"; 8 | import type { AutoBuildFile } from "./types.ts"; 9 | import { 10 | formatNumber, 11 | formatRed, 12 | highlight, 13 | protect, 14 | spawn, 15 | toExecCmd, 16 | } from "./utils.ts"; 17 | import { OPENCV_PATHS_ENV } from "./misc.ts"; 18 | import { getArch, getEnv, Platfrm } from "./env.ts"; 19 | 20 | export class SetupOpencv { 21 | constructor(private readonly builder: OpenCVBuilder) {} 22 | 23 | private getMsbuildCmd(sln: string): string[] { 24 | return [ 25 | sln, 26 | "/p:Configuration=Release", 27 | `/p:Platform=${getArch().includes("64") ? "x64" : "x86"}`, 28 | ]; 29 | } 30 | 31 | private async runBuildCmd(msbuildExe?: string): Promise { 32 | const env = this.builder.env; 33 | if (msbuildExe) { 34 | if (!fs.existsSync(msbuildExe)) { 35 | Log.log("error", "install", 'invalid msbuildExe path" %s', msbuildExe); 36 | throw Error("invalid msbuildExe path " + msbuildExe); 37 | } 38 | const buildSLN = this.getMsbuildCmd("./OpenCV.sln"); 39 | let args = toExecCmd(msbuildExe, buildSLN); 40 | this.execLog.push(`cd ${protect(env.opencvBuild)}`); 41 | this.execLog.push(args); 42 | if (!env.dryRun) { 43 | Log.log("info", "install", "spawning in %s: %s", env.opencvBuild, args); 44 | await spawn(`${msbuildExe}`, buildSLN, { cwd: env.opencvBuild }); 45 | } 46 | const buildVcxproj = this.getMsbuildCmd("./INSTALL.vcxproj"); 47 | args = toExecCmd(msbuildExe, buildVcxproj); 48 | this.execLog.push(`${args}`); 49 | if (!env.dryRun) { 50 | Log.log("info", "install", "spawning in %s: %s", env.opencvBuild, args); 51 | await spawn(`${msbuildExe}`, buildVcxproj, { cwd: env.opencvBuild }); 52 | } 53 | } else { 54 | this.execLog.push(`cd ${protect(env.opencvBuild)}`); 55 | this.execLog.push(`make install -j${env.numberOfCoresAvailable()}`); 56 | 57 | if (!env.dryRun) { 58 | Log.log("info", "install", "spawning in %s: make", env.opencvBuild); 59 | await spawn("make", ["install", `-j${env.numberOfCoresAvailable()}`], { 60 | cwd: env.opencvBuild, 61 | }); 62 | } 63 | 64 | this.execLog.push(`make all -j${env.numberOfCoresAvailable()}`); 65 | // revert the strange archiving of libopencv.so going on with make install 66 | if (!env.dryRun) { 67 | Log.log("info", "install", "spawning in %s: make all", env.opencvBuild); 68 | await spawn("make", ["all", `-j${env.numberOfCoresAvailable()}`], { 69 | cwd: env.opencvBuild, 70 | }); 71 | } 72 | } 73 | } 74 | 75 | private getWinCmakeFlags(msversion: string): string[] { 76 | const cmakeVsCompiler = this.builder.constant.cmakeVsCompilers[msversion]; 77 | const cmakeArch = this.builder.constant.cmakeArchs[getArch()]; 78 | 79 | if (!cmakeVsCompiler) { 80 | throw new Error( 81 | `no cmake Visual Studio compiler found for msversion: ${msversion}`, 82 | ); 83 | } 84 | if (!cmakeArch) { 85 | throw new Error(`no cmake arch found for arch: ${getArch()}`); 86 | } 87 | 88 | let GFlag: string[] = []; 89 | if (Number(msversion) <= 15) { 90 | GFlag = ["-G", `${cmakeVsCompiler}${cmakeArch}`]; 91 | } else { 92 | GFlag = ["-G", `${cmakeVsCompiler}`]; 93 | } 94 | return GFlag.concat(this.builder.env.getSharedCmakeFlags()); 95 | } 96 | 97 | private getCmakeArgs(cmakeFlags: string[]): string[] { 98 | Log.log( 99 | "info", 100 | "install", 101 | `getCmakeArgs prefixing Cmake args with ${highlight("%s")}`, 102 | this.builder.env.opencvSrc, 103 | ); 104 | return [this.builder.env.opencvSrc].concat(cmakeFlags); 105 | } 106 | 107 | private async getMsbuildIfWin(): Promise { 108 | if (Platfrm.isWindows) { 109 | const msbuilds = await findMSBuild(); 110 | if (msbuilds.length > 1) { 111 | Log.log( 112 | "warn", 113 | "install", 114 | `${msbuilds.length} msbuild version detected using the most recent one.`, 115 | ); 116 | } 117 | Log.log( 118 | "info", 119 | "install", 120 | `using msbuild V${formatNumber("%s")} path: ${highlight("%s")}`, 121 | msbuilds[0].version, 122 | msbuilds[0].path, 123 | ); 124 | return msbuilds[0]; 125 | } 126 | return undefined; 127 | } 128 | /** 129 | * Write Build Context to disk, to avoid further rebuild 130 | * @returns AutoBuildFile 131 | */ 132 | public writeAutoBuildFile( 133 | overwrite: boolean, 134 | buildLog?: string, 135 | ): AutoBuildFile { 136 | const env = this.builder.env; 137 | const autoBuildInfo: AutoBuildFile = { 138 | opencvVersion: env.opencvVersion, 139 | autoBuildFlags: env.autoBuildFlags, 140 | modules: this.builder.getLibs.getLibs(), 141 | env: this.builder.env.dumpEnv(), 142 | }; 143 | Log.log( 144 | "info", 145 | "install", 146 | `writing auto-build file into directory: ${highlight("%s")}`, 147 | env.autoBuildFile, 148 | ); 149 | // Log.log("info", 'install', JSON.stringify(autoBuildFile)) 150 | fs.mkdirSync(env.opencvRoot, { recursive: true }); 151 | if (!overwrite) { 152 | const old = env.readAutoBuildFile(); 153 | if (old) { 154 | return old; 155 | } 156 | } 157 | fs.writeFileSync(env.autoBuildFile, JSON.stringify(autoBuildInfo, null, 4)); 158 | if (buildLog) { 159 | fs.writeFileSync(env.autoBuildLog, buildLog); 160 | } 161 | return autoBuildInfo; 162 | } 163 | 164 | /** 165 | * add a sym link named latest to the current build. 166 | */ 167 | public linkBuild(): void { 168 | const env = this.builder.env; 169 | const latest = path.join(env.rootDir, "latest"); 170 | try { 171 | fs.unlinkSync(latest); 172 | } catch (_e) { 173 | // ignore 174 | } 175 | try { 176 | fs.symlinkSync(env.opencvRoot, latest); 177 | Log.log( 178 | "info", 179 | "install", 180 | `Cretate link ${highlight("%s")} to ${highlight("%s")}`, 181 | latest, 182 | env.opencvRoot, 183 | ); 184 | } catch (e) { 185 | Log.log( 186 | "info", 187 | "install", 188 | `Failed to create link ${highlight("%s")} to ${ 189 | highlight("%s") 190 | } Error: ${formatRed("%s")}`, 191 | latest, 192 | env.opencvRoot, 193 | (e as Error).message, 194 | ); 195 | } 196 | } 197 | 198 | private execLog: string[] = []; 199 | 200 | /** 201 | * clone OpenCV repo 202 | * build OpenCV 203 | * delete source files 204 | */ 205 | public async start(): Promise { 206 | this.execLog = []; 207 | const env = this.builder.env; 208 | const msbuild = await this.getMsbuildIfWin(); 209 | let cMakeFlags: string[] = []; 210 | let msbuildPath = ""; 211 | // Get cmake flags here to check for CUDA early on instead of the start of the building process 212 | if (Platfrm.isWindows) { 213 | if (!msbuild) { 214 | throw Error("Error getting Ms Build info"); 215 | } 216 | cMakeFlags = this.getWinCmakeFlags("" + msbuild.version); 217 | msbuildPath = msbuild.path; 218 | } else { 219 | cMakeFlags = this.builder.env.getSharedCmakeFlags(); 220 | } 221 | Log.log( 222 | "info", 223 | "install", 224 | `cMakeFlags will be: ${formatNumber("%s")}`, 225 | cMakeFlags.join(" "), 226 | ); 227 | 228 | const tag = env.opencvVersion; 229 | Log.log( 230 | "info", 231 | "install", 232 | `installing opencv version ${formatNumber("%s")} into directory: ${ 233 | highlight("%s") 234 | }`, 235 | tag, 236 | env.opencvRoot, 237 | ); 238 | Log.log( 239 | "info", 240 | "install", 241 | `Cleaning old build: src, build and contrib-src directories`, 242 | ); 243 | try { 244 | for (const k of OPENCV_PATHS_ENV) { 245 | const v = getEnv(k); 246 | if (v) { 247 | const setEnv = Platfrm.isWindows ? "$Env:" : "export "; 248 | this.execLog.push(`${setEnv}${k}=${protect(v)}`); 249 | } 250 | } 251 | // clean up 252 | const dirs = [env.opencvBuild, env.opencvSrc, env.opencvContribSrc]; 253 | this.execLog.push(toExecCmd("rimraf", dirs)); 254 | for (const dir of dirs) { 255 | await rimraf(dir); 256 | } 257 | // ensure build dir exists 258 | this.execLog.push(toExecCmd("mkdir", ["-p", env.opencvBuild])); 259 | fs.mkdirSync(env.opencvBuild, { recursive: true }); 260 | 261 | // hide detached HEAD message. 262 | const gitFilter = (data: Uint8Array): Uint8Array | null => { 263 | const asTxt = data.toString(); 264 | if (asTxt.includes("detached HEAD")) return null; 265 | if (asTxt.includes("--depth is ignored in local clones")) return null; 266 | return data; 267 | }; 268 | 269 | if (env.isWithoutContrib) { 270 | this.execLog.push(toExecCmd("cd", [env.opencvRoot])); 271 | Log.log( 272 | "info", 273 | "install", 274 | `skipping download of opencv_contrib since ${ 275 | highlight("OPENCV4NODEJS_AUTOBUILD_WITHOUT_CONTRIB") 276 | } is set`, 277 | ); 278 | } else { 279 | let opencvContribRepoUrl = this.builder.constant.opencvContribRepoUrl; 280 | if (this.builder.env.gitCache) { 281 | if (!fs.existsSync(this.builder.env.opencvContribGitCache)) { 282 | const args = [ 283 | "clone", 284 | "--quiet", 285 | "--progress", 286 | opencvContribRepoUrl, 287 | this.builder.env.opencvContribGitCache, 288 | ]; 289 | await spawn("git", args, { cwd: env.opencvRoot }, { 290 | err: gitFilter, 291 | }); 292 | } else { 293 | await spawn("git", ["pull"], { cwd: env.opencvContribGitCache }, { 294 | err: gitFilter, 295 | }); 296 | } 297 | opencvContribRepoUrl = env.opencvContribGitCache.replace(/\\/g, "/"); 298 | } 299 | Log.log("info", "install", `git clone ${opencvContribRepoUrl}`); 300 | const args = [ 301 | "clone", 302 | "--quiet", 303 | "-b", 304 | `${tag}`, 305 | "--single-branch", 306 | "--depth", 307 | "1", 308 | "--progress", 309 | opencvContribRepoUrl, 310 | env.opencvContribSrc, 311 | ]; 312 | this.execLog.push(toExecCmd("cd", [env.opencvRoot])); 313 | this.execLog.push(toExecCmd("git", args)); 314 | await spawn("git", args, { cwd: env.opencvRoot }, { err: gitFilter }); 315 | } 316 | let opencvRepoUrl = this.builder.constant.opencvRepoUrl; 317 | 318 | if (this.builder.env.gitCache) { 319 | if (!fs.existsSync(this.builder.env.opencvGitCache)) { 320 | const args = [ 321 | "clone", 322 | "--quiet", 323 | "--progress", 324 | opencvRepoUrl, 325 | this.builder.env.opencvGitCache, 326 | ]; 327 | await spawn("git", args, { cwd: env.opencvRoot }, { err: gitFilter }); 328 | } else { 329 | await spawn("git", ["pull"], { cwd: env.opencvGitCache }, { 330 | err: gitFilter, 331 | }); 332 | } 333 | opencvRepoUrl = env.opencvGitCache.replace(/\\/g, "/"); 334 | } 335 | 336 | Log.log("info", "install", `git clone ${opencvRepoUrl}`); 337 | const args2 = [ 338 | "clone", 339 | "--quiet", 340 | "-b", 341 | `${tag}`, 342 | "--single-branch", 343 | "--depth", 344 | "1", 345 | "--progress", 346 | opencvRepoUrl, 347 | env.opencvSrc, 348 | ]; 349 | this.execLog.push(toExecCmd("git", args2)); 350 | await spawn("git", args2, { cwd: env.opencvRoot }, { err: gitFilter }); 351 | 352 | this.execLog.push(`export OPENCV_BIN_DIR=${protect(env.opencvBinDir)}`); 353 | this.execLog.push( 354 | `export OPENCV_INCLUDE_DIR=${protect(env.opencvIncludeDir)}`, 355 | ); 356 | this.execLog.push(`export OPENCV_LIB_DIR=${protect(env.opencvLibDir)}`); 357 | 358 | const cmakeArgs = this.getCmakeArgs(cMakeFlags); 359 | 360 | Log.log( 361 | "info", 362 | "install", 363 | "running in %s cmake %s", 364 | protect(env.opencvBuild), 365 | cmakeArgs.map(protect).join(" "), 366 | ); 367 | this.execLog.push(toExecCmd("cd", [env.opencvBuild])); 368 | this.execLog.push(toExecCmd("cmake", cmakeArgs)); 369 | if (!env.dryRun) { 370 | await spawn("cmake", cmakeArgs, { cwd: env.opencvBuild }); 371 | Log.log("info", "install", "starting build..."); 372 | } 373 | await this.runBuildCmd(msbuildPath); 374 | } catch (e) { 375 | const allCmds = this.execLog.join(EOL); 376 | Log.log( 377 | "error", 378 | "build", 379 | `Compilation failed, previous calls:${EOL}%s`, 380 | allCmds, 381 | ); 382 | // Log.log("error", `Compilation failed, previous calls:${EOL}%s`, allCmds); 383 | throw e; 384 | } 385 | 386 | if (!env.dryRun) { 387 | this.writeAutoBuildFile(true, this.execLog.join(EOL)); 388 | this.linkBuild(); 389 | } else { 390 | this.execLog.push("echo lock file can not be generated in dry-mode"); 391 | } 392 | // cmake -D CMAKE_BUILD_TYPE=RELEASE -D ENABLE_NEON=ON 393 | // -D ENABLE_TBB=ON -D ENABLE_IPP=ON -D ENABLE_VFVP3=ON -D WITH_OPENMP=ON -D WITH_CSTRIPES=ON -D WITH_OPENCL=ON -D CMAKE_INSTALL_PREFIX=/usr/local 394 | // -D OPENCV_EXTRA_MODULES_PATH=/root/[username]/opencv_contrib-3.4.0/modules/ .. 395 | if (!env.keepsources && !env.dryRun) { 396 | /** 397 | * DELETE TMP build dirs 398 | */ 399 | try { 400 | Log.log( 401 | "info", 402 | "install", 403 | `cleaning openCV build file in ${ 404 | highlight("%s") 405 | } to keep these files enable keepsources with ${ 406 | highlight("--keepsources") 407 | }`, 408 | env.opencvSrc, 409 | ); 410 | await rimraf(env.opencvSrc); 411 | } catch (err) { 412 | Log.log( 413 | "error", 414 | "install", 415 | "failed to clean opencv source folder:", 416 | err, 417 | ); 418 | Log.log( 419 | "error", 420 | "install", 421 | `consider removing the folder yourself: ${highlight("%s")}`, 422 | env.opencvSrc, 423 | ); 424 | } 425 | 426 | try { 427 | Log.log( 428 | "info", 429 | "install", 430 | `cleaning openCVContrib build file in ${ 431 | highlight("%s") 432 | } to keep these files enable keepsources with ${ 433 | highlight("--keepsources") 434 | }`, 435 | env.opencvContribSrc, 436 | ); 437 | await rimraf(env.opencvContribSrc); 438 | } catch (err) { 439 | Log.log( 440 | "error", 441 | "install", 442 | "failed to clean opencv_contrib source folder:", 443 | err, 444 | ); 445 | Log.log( 446 | "error", 447 | "install", 448 | `consider removing the folder yourself: ${highlight("%s")}`, 449 | env.opencvContribSrc, 450 | ); 451 | } 452 | } else { 453 | Log.log( 454 | "info", 455 | "install", 456 | `Keeping openCV build file in ${highlight("%s")}`, 457 | env.opencvSrc, 458 | ); 459 | Log.log( 460 | "info", 461 | "install", 462 | `Keeping openCVContrib build file in ${highlight("%s")}`, 463 | env.opencvContribSrc, 464 | ); 465 | } 466 | if (env.dryRun) { 467 | console.log(); 468 | console.log(); 469 | console.log(this.execLog.join(EOL)); 470 | } 471 | } 472 | } 473 | -------------------------------------------------------------------------------- /src/misc.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * options passed to OpenCVBuildEnv constructor 3 | * highest priority values 4 | */ 5 | export interface OpenCVBuildEnvParamsBool { 6 | autoBuildBuildCuda?: boolean; 7 | autoBuildWithoutContrib?: boolean; 8 | disableAutoBuild?: boolean; 9 | keepsources?: boolean; 10 | verbose?: boolean; 11 | "dry-run"?: boolean; 12 | "git-cache"?: boolean; 13 | } 14 | 15 | type boolKey = keyof OpenCVBuildEnvParamsBool; 16 | 17 | export interface OpenCVBuildEnvParamsString { 18 | /** 19 | * OpenCV-build root directory, deprecated in favor of buildRoot 20 | */ 21 | rootcwd?: string; 22 | /** 23 | * OpenCV build directory, this directory will be populate with a folder per build, permiting to reused previous build. 24 | */ 25 | buildRoot?: string; 26 | /** 27 | * OpenCV version to build 28 | */ 29 | autoBuildOpencvVersion?: string; 30 | /** 31 | * OpenCV cMake Build flags 32 | */ 33 | autoBuildFlags?: string; 34 | /** 35 | * OpenCV include directory 36 | * looks like: opencv/build/include 37 | */ 38 | opencvIncludeDir?: string; // or external build apt / brew / yum / chocolatey... 39 | /** 40 | * OpenCV library directory 41 | * looks like: opencv/build/.../lib 42 | */ 43 | opencvLibDir?: string; // never used based on opencvBuild path + OS postfix 44 | /** 45 | * OpenCV bin directory 46 | * looks like: opencv/build/.../bin 47 | */ 48 | opencvBinDir?: string; // never used based on opencvBuild path + OS postfix 49 | /** 50 | * Restrict cuda targeded version to a limited set of version 51 | * add on 28/12/2022 52 | */ 53 | cudaArch?: string; 54 | } 55 | type stringKey = keyof OpenCVBuildEnvParamsString; 56 | 57 | /** 58 | * Options as usable in opencv4nodejs section from package.json 59 | * Middle priority values 60 | */ 61 | export type OpenCVPackageBuildOptions = 62 | & { [key in boolKey | stringKey]?: string } 63 | & { 64 | prebuild?: 65 | | "latestBuild" 66 | | "latestVersion" 67 | | "oldestBuild" 68 | | "oldestVersion"; 69 | }; 70 | 71 | export interface OpenCVBuildEnvParams 72 | extends OpenCVBuildEnvParamsBool, OpenCVBuildEnvParamsString { 73 | /** 74 | * Allow speedup API usage by allowing direct access to a preexisting build 75 | */ 76 | prebuild?: "latestBuild" | "latestVersion" | "oldestBuild" | "oldestVersion"; 77 | extra?: { [key: string]: string }; 78 | } 79 | 80 | /** 81 | * local args parser model 82 | */ 83 | export interface ArgInfo { 84 | arg: string; 85 | conf: keyof OpenCVPackageBuildOptions; 86 | env: string; 87 | isBool: boolean; 88 | doc: string; 89 | } 90 | 91 | /** 92 | * list of variables needed to link and use openCV 93 | */ 94 | export const OPENCV_PATHS_ENV = [ 95 | "OPENCV_BIN_DIR", 96 | "OPENCV_INCLUDE_DIR", 97 | "OPENCV_LIB_DIR", 98 | ] as const; 99 | 100 | /** 101 | * arguments data 102 | * key must be === arg 103 | */ 104 | export const ALLARGS = { 105 | version: { 106 | arg: "version", 107 | conf: "autoBuildOpencvVersion", 108 | env: "OPENCV4NODEJS_AUTOBUILD_OPENCV_VERSION", 109 | isBool: false, 110 | doc: "OpenCV version", 111 | } as ArgInfo, 112 | verbose: { arg: "verbose", isBool: false, doc: "verbose" } as ArgInfo, 113 | flags: { 114 | arg: "flags", 115 | conf: "autoBuildFlags", 116 | env: "OPENCV4NODEJS_AUTOBUILD_FLAGS", 117 | isBool: false, 118 | doc: "OpenCV cMake Build flags", 119 | } as ArgInfo, 120 | root: { 121 | arg: "root", 122 | conf: "rootcwd", 123 | env: "INIT_CWD", 124 | isBool: false, 125 | doc: "OpenCV-build root directory (deprecated)", 126 | } as ArgInfo, 127 | prebuild: { 128 | arg: "prebuild", 129 | conf: "prebuild", 130 | env: "OPENCV_PREBUILD", 131 | isBool: false, 132 | doc: 133 | "force prebuild param: latestBuild|latestVersion|oldestBuild|oldestVersion", 134 | } as ArgInfo, 135 | buildRoot: { 136 | arg: "buildRoot", 137 | conf: "buildRoot", 138 | env: "OPENCV_BUILD_ROOT", 139 | isBool: false, 140 | doc: "OpenCV build directory", 141 | } as ArgInfo, 142 | cuda: { 143 | arg: "cuda", 144 | conf: "autoBuildBuildCuda", 145 | env: "OPENCV4NODEJS_BUILD_CUDA", 146 | isBool: true, 147 | doc: "Enable cuda in OpenCV build", 148 | } as ArgInfo, 149 | // add on 28/12/2022 150 | cudaArch: { 151 | arg: "cudaArch", 152 | conf: "cudaArch", 153 | env: "OPENCV4NODEJS_BUILD_CUDA_ARCH", 154 | isBool: false, 155 | doc: 156 | "Specify the cuda arch will drasticly reduce build time, see https://en.wikipedia.org/wiki/CUDA, ex if you have a RTX 3080 use --cudaArch=8.6, if you have also a RTX 2080 --cudaArch=7.5,8.6", 157 | } as ArgInfo, 158 | nocontrib: { 159 | arg: "nocontrib", 160 | conf: "autoBuildWithoutContrib", 161 | env: "OPENCV4NODEJS_AUTOBUILD_WITHOUT_CONTRIB", 162 | isBool: true, 163 | doc: "Do not compile Contrib modules", 164 | } as ArgInfo, 165 | nobuild: { 166 | arg: "nobuild", 167 | conf: "disableAutoBuild", 168 | env: "OPENCV4NODEJS_DISABLE_AUTOBUILD", 169 | isBool: true, 170 | doc: "Do build OpenCV", 171 | } as ArgInfo, 172 | incDir: { 173 | arg: "incDir", 174 | conf: "opencvIncludeDir", 175 | env: "OPENCV_INCLUDE_DIR", 176 | isBool: false, 177 | doc: "OpenCV include directory", 178 | } as ArgInfo, 179 | libDir: { 180 | arg: "libDir", 181 | conf: "opencvLibDir", 182 | env: "OPENCV_LIB_DIR", 183 | isBool: false, 184 | doc: "OpenCV library directory", 185 | } as ArgInfo, 186 | binDir: { 187 | arg: "binDir", 188 | conf: "opencvBinDir", 189 | env: "OPENCV_BIN_DIR", 190 | isBool: false, 191 | doc: "OpenCV bin directory", 192 | } as ArgInfo, 193 | keepsources: { 194 | arg: "keepsources", 195 | conf: "keepsources", 196 | isBool: true, 197 | doc: "Keepsources OpenCV source after build", 198 | } as ArgInfo, 199 | "dry-run": { 200 | arg: "dry-run", 201 | conf: "dry-run", 202 | isBool: true, 203 | doc: "Display command line use to build library", 204 | } as ArgInfo, 205 | "git-cache": { 206 | arg: "git-cache", 207 | conf: "git-cache", 208 | env: "OPENCV_GIT_CACHE", 209 | isBool: true, 210 | doc: "Reduce Bandwide usage, by keeping a local git souce un the buildRoot", 211 | } as ArgInfo, 212 | }; 213 | /** 214 | * generate help message 215 | * @returns help message as text with colors 216 | */ 217 | export const genHelp = (): string => { 218 | return Object.values(ALLARGS).map((a) => { 219 | const name = `--${a.arg}${!a.isBool ? " " : ""}`; 220 | const envWay = a.env ? ` (${a.env} env variable)` : ""; 221 | return ` ${name.padEnd(20)} ${a.doc.padEnd(40)}${envWay}`; 222 | }).join("\n"); 223 | }; 224 | /** 225 | * A basic args parser 226 | * @param args cmd lines args 227 | * @returns and openCVBuildEnvParams object containing an extra object with all unknown args 228 | */ 229 | export const args2Option = (args: string[]): OpenCVBuildEnvParams => { 230 | const out: OpenCVBuildEnvParams = { extra: {} }; 231 | for (let i = 0; i < args.length; i++) { 232 | let arg = args[i]; 233 | if (arg.startsWith("--")) { 234 | arg = arg.substring(2); 235 | } else if (arg.startsWith("-")) { 236 | arg = arg.substring(1); 237 | } else { 238 | continue; 239 | } 240 | const p = arg.indexOf("="); 241 | const name = (p === -1) ? arg : arg.substring(0, p); 242 | const info = ALLARGS[name as keyof typeof ALLARGS]; 243 | if (!info) { 244 | // keep unknown args in extras 245 | const val = (p > 0) 246 | ? arg.substring(p + 1) 247 | : (i + 1 < args.length) 248 | ? args[i + 1] 249 | : "1"; 250 | if (out.extra) { 251 | out.extra[name] = val; 252 | } 253 | continue; 254 | } 255 | if (info.isBool) { 256 | out[info.conf as boolKey] = true; 257 | continue; 258 | } 259 | const val = (p > 0) ? arg.substring(p + 1) : args[++i]; 260 | if (val) { 261 | out[info.conf as stringKey] = val; 262 | } 263 | } 264 | // encvIncludeDir?: string; 265 | // console.log(out); 266 | return out; 267 | }; 268 | 269 | /** 270 | * from https://docs.opencv.org/4.x/ 271 | */ 272 | export const MODEULES_MAP = { 273 | // not a real module 274 | // apps: true, 275 | // not a real module 276 | world: false, 277 | // was enabled in previous version. 278 | python_tests: true, 279 | 280 | /** 281 | * Core functionality 282 | * https://docs.opencv.org/4.x/d0/de1/group__core.html 283 | */ 284 | core: true, 285 | /** 286 | * Image Processing 287 | * https://docs.opencv.org/4.x/d7/dbd/group__imgproc.html 288 | */ 289 | imgproc: true, 290 | /** 291 | * Image file reading and writing 292 | * https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html 293 | */ 294 | imgcodecs: true, 295 | /** 296 | * Video I/O 297 | * https://docs.opencv.org/4.x/dd/de7/group__videoio.html 298 | */ 299 | videoio: true, 300 | /** 301 | * High-level GUI 302 | * https://docs.opencv.org/4.x/d7/dfc/group__highgui.html 303 | */ 304 | highgui: true, 305 | /** 306 | * Video Analysis 307 | * https://docs.opencv.org/4.x/d7/de9/group__video.html 308 | */ 309 | video: true, 310 | /** 311 | * Camera Calibration and 3D Reconstruction 312 | * https://docs.opencv.org/4.x/d9/d0c/group__calib3d.html 313 | */ 314 | calib3d: true, 315 | /** 316 | * 2D Features Framework 317 | * https://docs.opencv.org/4.x/da/d9b/group__features2d.html 318 | */ 319 | features2d: true, 320 | /** 321 | * Object Detection 322 | * https://docs.opencv.org/4.x/d5/d54/group__objdetect.html 323 | */ 324 | objdetect: true, 325 | /** 326 | * Deep Neural Network module 327 | * https://docs.opencv.org/4.x/d6/d0f/group__dnn.html 328 | */ 329 | dnn: true, 330 | /** 331 | * Machine Learning 332 | * https://docs.opencv.org/4.x/dd/ded/group__ml.html 333 | */ 334 | ml: true, 335 | /** 336 | * Clustering and Search in Multi-Dimensional Spaces 337 | * https://docs.opencv.org/4.x/dc/de5/group__flann.html 338 | */ 339 | flann: true, 340 | /** 341 | * Computational Photography 342 | * https://docs.opencv.org/4.x/d1/d0d/group__photo.html 343 | */ 344 | photo: true, 345 | /** 346 | * Images stitching 347 | * https://docs.opencv.org/4.x/d1/d46/group__stitching.html 348 | */ 349 | stitching: false, 350 | /** 351 | * Graph API 352 | * https://docs.opencv.org/4.x/d0/d1e/gapi.html 353 | */ 354 | gapi: true, 355 | /** 356 | * Extra modules 357 | */ 358 | /** 359 | * Alpha Matting 360 | * https://docs.opencv.org/4.x/d4/d40/group__alphamat.html 361 | */ 362 | alphamat: null, 363 | /** 364 | * Aruco markers, module functionality was moved to objdetect module 365 | * https://docs.opencv.org/4.x/d9/d6a/group__aruco.html 366 | */ 367 | aruco: false, 368 | /** 369 | * Barcode detecting and decoding methods 370 | * https://docs.opencv.org/4.x/d2/dea/group__barcode.html 371 | */ 372 | barcode: null, 373 | /** 374 | * Improved Background-Foreground Segmentation Methods 375 | * https://docs.opencv.org/4.x/d2/d55/group__bgsegm.html 376 | */ 377 | bgsegm: false, 378 | /** 379 | * Biologically inspired vision models and derivated tools 380 | * https://docs.opencv.org/4.x/dd/deb/group__bioinspired.html 381 | */ 382 | bioinspired: false, 383 | /** 384 | * Custom Calibration Pattern for 3D reconstruction 385 | * https://docs.opencv.org/4.x/d3/ddc/group__ccalib.html 386 | */ 387 | ccalib: false, 388 | /** 389 | * Operations on Matrices 390 | * https://docs.opencv.org/4.x/d5/d8e/group__cudaarithm.html 391 | */ 392 | cudaarithm: null, 393 | /** 394 | * Background Segmentation 395 | * https://docs.opencv.org/4.x/d6/d17/group__cudabgsegm.html 396 | */ 397 | cudabgsegm: null, 398 | /** 399 | * Video Encoding/Decoding 400 | * https://docs.opencv.org/4.x/d0/d61/group__cudacodec.html 401 | */ 402 | cudacodec: null, 403 | /** 404 | * Feature Detection and Description 405 | * https://docs.opencv.org/4.x/d6/d1d/group__cudafeatures2d.html 406 | */ 407 | cudafeatures2d: null, 408 | /** 409 | * Image Filtering 410 | * https://docs.opencv.org/4.x/dc/d66/group__cudafilters.html 411 | */ 412 | cudafilters: null, 413 | /** 414 | * Image Processing 415 | * https://docs.opencv.org/4.x/d0/d05/group__cudaimgproc.html 416 | */ 417 | cudaimgproc: null, 418 | /** 419 | * Legacy support 420 | * https://docs.opencv.org/4.x/d5/dc3/group__cudalegacy.html 421 | */ 422 | cudalegacy: null, 423 | /** 424 | * Object Detection 425 | * https://docs.opencv.org/4.x/d9/d3f/group__cudaobjdetect.html 426 | */ 427 | cudaobjdetect: null, 428 | /** 429 | * Optical Flow 430 | * https://docs.opencv.org/4.x/d7/d3f/group__cudaoptflow.html 431 | */ 432 | cudaoptflow: null, 433 | /** 434 | * Stereo Correspondence 435 | * https://docs.opencv.org/4.x/dd/d47/group__cudastereo.html 436 | */ 437 | cudastereo: null, 438 | /** 439 | * Image Warping 440 | * https://docs.opencv.org/4.x/db/d29/group__cudawarping.html 441 | */ 442 | cudawarping: null, 443 | /** 444 | * Device layer 445 | * https://docs.opencv.org/4.x/df/dfc/group__cudev.html 446 | */ 447 | cudev: null, 448 | /** 449 | * GUI for Interactive Visual Debugging of Computer Vision Programs 450 | * https://docs.opencv.org/4.x/df/dff/group__cvv.html 451 | */ 452 | cvv: null, 453 | /** 454 | * Framework for working with different datasets 455 | * https://docs.opencv.org/4.x/d8/d00/group__datasets.html 456 | */ 457 | datasets: false, 458 | /** 459 | * DNN used for object detection 460 | * https://docs.opencv.org/4.x/d5/df6/group__dnn__objdetect.html 461 | */ 462 | dnn_objdetect: false, 463 | /** 464 | * DNN used for super resolution 465 | * https://docs.opencv.org/4.x/d9/de0/group__dnn__superres.html 466 | */ 467 | dnn_superres: null, 468 | /** 469 | * Deformable Part-based Models 470 | * https://docs.opencv.org/4.x/d9/d12/group__dpm.html 471 | */ 472 | dpm: false, 473 | /** 474 | * Face Analysis 475 | * https://docs.opencv.org/4.x/db/d7c/group__face.html 476 | */ 477 | face: true, 478 | /** 479 | * Drawing UTF-8 strings with freetype/harfbuzz 480 | * https://docs.opencv.org/4.x/d4/dfc/group__freetype.html 481 | */ 482 | freetype: null, // activated by default ? 483 | /** 484 | * Image processing based on fuzzy mathematics 485 | * https://docs.opencv.org/4.x/df/d5b/group__fuzzy.html 486 | */ 487 | fuzzy: false, 488 | /** 489 | * Hierarchical Data Format I/O routines 490 | * https://docs.opencv.org/4.x/db/d77/group__hdf.html 491 | */ 492 | hdf: null, 493 | /** 494 | * Hierarchical Feature Selection for Efficient Image Segmentation 495 | * https://docs.opencv.org/4.x/dc/d29/group__hfs.html 496 | */ 497 | hfs: false, 498 | /** 499 | * The module brings implementations of different image hashing algorithms. 500 | * https://docs.opencv.org/4.x/d4/d93/group__img__hash.html 501 | */ 502 | img_hash: true, 503 | /** 504 | * The module brings implementations of intensity transformation algorithms to adjust image contrast. 505 | * https://docs.opencv.org/4.x/dc/dfe/group__intensity__transform.html 506 | */ 507 | intensity_transform: null, 508 | /** 509 | * Julia bindings for OpenCV 510 | * https://docs.opencv.org/4.x/d7/d44/group__julia.html 511 | */ 512 | julia: null, 513 | /** 514 | * Binary descriptors for lines extracted from an image 515 | * https://docs.opencv.org/4.x/dc/ddd/group__line__descriptor.html 516 | */ 517 | line_descriptor: false, 518 | /** 519 | * Macbeth Chart module 520 | * https://docs.opencv.org/4.x/dd/d19/group__mcc.html 521 | */ 522 | mcc: null, 523 | /** 524 | * Optical Flow Algorithms 525 | * https://docs.opencv.org/4.x/d2/d84/group__optflow.html 526 | */ 527 | optflow: false, 528 | /** 529 | * OGRE 3D Visualiser 530 | * https://docs.opencv.org/4.x/d2/d17/group__ovis.html 531 | */ 532 | ovis: null, 533 | /** 534 | * Phase Unwrapping API 535 | * https://docs.opencv.org/4.x/df/d3a/group__phase__unwrapping.html 536 | */ 537 | phase_unwrapping: false, 538 | /** 539 | * Plot function for Mat data 540 | * https://docs.opencv.org/4.x/db/dfe/group__plot.html 541 | */ 542 | plot: null, 543 | /* 544 | * Image Quality Analysis (IQA) API 545 | * https://docs.opencv.org/4.x/dc/d20/group__quality.html 546 | */ 547 | quality: null, 548 | /** 549 | * silhouette based 3D object tracking 550 | * https://docs.opencv.org/4.x/d4/dc4/group__rapid.html 551 | */ 552 | rapid: null, 553 | /** 554 | * Image Registration 555 | * https://docs.opencv.org/4.x/db/d61/group__reg.html 556 | */ 557 | reg: false, 558 | /** 559 | * RGB-Depth Processing 560 | * https://docs.opencv.org/4.x/d2/d3a/group__rgbd.html 561 | */ 562 | rgbd: false, 563 | /** 564 | * Saliency API 565 | * https://docs.opencv.org/4.x/d8/d65/group__saliency.html 566 | */ 567 | saliency: false, 568 | /** 569 | * Structure From Motion 570 | * https://docs.opencv.org/4.x/d8/d8c/group__sfm.html 571 | */ 572 | sfm: null, 573 | /** 574 | * Shape Distance and Matching 575 | * https://docs.opencv.org/4.x/d1/d85/group__shape.html 576 | */ 577 | shape: false, 578 | /** 579 | * Stereo Correspondance Algorithms 580 | * https://docs.opencv.org/4.x/dd/d86/group__stereo.html 581 | */ 582 | stereo: false, 583 | /** 584 | * Structured Light API 585 | * https://docs.opencv.org/4.x/d1/d90/group__structured__light.html 586 | */ 587 | structured_light: false, 588 | /** 589 | * Super Resolution 590 | * https://docs.opencv.org/4.x/d7/d0a/group__superres.html 591 | */ 592 | superres: false, 593 | /** 594 | * Surface Matching 595 | * https://docs.opencv.org/4.x/d9/d25/group__surface__matching.html 596 | */ 597 | surface_matching: false, 598 | /** 599 | * Scene Text Detection and Recognition 600 | * https://docs.opencv.org/4.x/d4/d61/group__text.html 601 | */ 602 | text: true, 603 | /** 604 | * Tracking API 605 | * https://docs.opencv.org/4.x/d9/df8/group__tracking.html 606 | */ 607 | tracking: true, 608 | /** 609 | * Video Stabilization 610 | * https://docs.opencv.org/4.x/d5/d50/group__videostab.html 611 | */ 612 | videostab: true, 613 | /** 614 | * 3D Visualizer 615 | * https://docs.opencv.org/4.x/d1/d19/group__viz.html 616 | */ 617 | viz: null, 618 | /** 619 | * WeChat QR code detector for detecting and parsing QR code. 620 | * https://docs.opencv.org/4.x/dd/d63/group__wechat__qrcode.html 621 | */ 622 | wechat_qrcode: false, 623 | /** 624 | * Extra 2D Features Framework 625 | * https://docs.opencv.org/4.x/d1/db4/group__xfeatures2d.html 626 | */ 627 | xfeatures2d: true, 628 | /** 629 | * Extended Image Processing 630 | * https://docs.opencv.org/4.x/df/d2d/group__ximgproc.html 631 | */ 632 | ximgproc: true, 633 | /** 634 | * Extended object detection 635 | * https://docs.opencv.org/4.x/d4/d54/group__xobjdetect.html 636 | */ 637 | xobjdetect: false, 638 | /** 639 | * Additional photo processing algorithms 640 | * https://docs.opencv.org/4.x/de/daa/group__xphoto.html 641 | */ 642 | xphoto: false, 643 | // olds: 644 | apps: false, 645 | java_bindings_generator: false, 646 | js: false, 647 | bindings_generator: false, 648 | objc_bindings_generator: false, 649 | python3: false, 650 | python_bindings_generator: false, 651 | ts: false, 652 | } as const; 653 | 654 | /** 655 | * type of valid openCV Modules 656 | */ 657 | export type OpencvModulesType = keyof typeof MODEULES_MAP; 658 | 659 | /** 660 | * All available module fron openCV 4.5.5 661 | */ 662 | export const ALL_OPENCV_MODULES = new Set( 663 | Object.keys(MODEULES_MAP) as OpencvModulesType[], 664 | ); 665 | -------------------------------------------------------------------------------- /src/OpenCVBuildEnv.ts: -------------------------------------------------------------------------------- 1 | import fs, { type Stats } from "node:fs"; 2 | import os from "node:os"; 3 | import path from "node:path"; 4 | import crypto from "node:crypto"; 5 | import { formatNumber, highlight, isCudaAvailable } from "./utils.ts"; 6 | import type { AutoBuildFile, EnvSummery } from "./types.ts"; 7 | import { ALLARGS, MODEULES_MAP, OPENCV_PATHS_ENV } from "./misc.ts"; 8 | import type { 9 | ArgInfo, 10 | OpenCVBuildEnvParams, 11 | OpenCVBuildEnvParamsBool, 12 | OpenCVBuildEnvParamsString, 13 | OpencvModulesType, 14 | OpenCVPackageBuildOptions, 15 | } from "./misc.ts"; 16 | 17 | import { ALL_OPENCV_MODULES } from "./misc.ts"; 18 | import * as detector from "./helper/detect.ts"; 19 | import { getEnv, Platfrm, setEnv } from "./env.ts"; 20 | import Log from "./Log.ts"; 21 | import StaticTools from "./StaticTools.ts"; 22 | import { pc } from "../deps.ts"; 23 | 24 | function toBool(value?: string | null) { 25 | if (!value) { 26 | return false; 27 | } 28 | if (typeof value === "boolean") { 29 | return value; 30 | } 31 | if (typeof value === "number") { 32 | return value > 0; 33 | } 34 | value = value.toLowerCase(); 35 | if ( 36 | value === "0" || value === "false" || value === "off" || 37 | value.startsWith("disa") 38 | ) { 39 | return false; 40 | } 41 | return true; 42 | } 43 | 44 | // const DEFAULT_OPENCV_VERSION = '4.6.0'; 45 | 46 | export default class OpenCVBuildEnv 47 | implements OpenCVBuildEnvParamsBool, OpenCVBuildEnvParamsString { 48 | public prebuild?: 49 | | "latestBuild" 50 | | "latestVersion" 51 | | "oldestBuild" 52 | | "oldestVersion"; 53 | /** 54 | * set using env OPENCV4NODEJS_AUTOBUILD_OPENCV_VERSION , or --version or autoBuildOpencvVersion option in package.json 55 | */ 56 | public opencvVersion: string; 57 | /** 58 | * set using env OPENCV4NODEJS_BUILD_CUDA , or --cuda or autoBuildBuildCuda option in package.json 59 | */ 60 | public buildWithCuda = false; 61 | #cudaArch = ""; 62 | 63 | get cudaArch(): string { 64 | const arch = this.#cudaArch; 65 | if (!arch) { 66 | return ""; 67 | } 68 | if (!arch.match(/^(\d+\.\d+)(,\d+\.\d+)*$/)) { 69 | throw Error( 70 | `invalid value for cudaArch "${arch}" should be a list of valid cuda arch separated by comma like: "7.5,8.6"`, 71 | ); 72 | } 73 | return arch; 74 | } 75 | /** 76 | * set using env OPENCV4NODEJS_AUTOBUILD_WITHOUT_CONTRIB, or --nocontrib arg, or autoBuildWithoutContrib option in package.json 77 | */ 78 | public isWithoutContrib = false; 79 | /** 80 | * set using env OPENCV4NODEJS_DISABLE_AUTOBUILD, or --nobuild arg or disableAutoBuild option in package.json 81 | */ 82 | public isAutoBuildDisabled = false; 83 | /** 84 | * set using --keepsources arg or keepsources option in package.json 85 | */ 86 | public keepsources = false; 87 | /** 88 | * set using --dry-run arg or dry-run option in package.json 89 | */ 90 | public dryRun = false; 91 | public gitCache = false; 92 | // root path to look for package.json opencv4nodejs section 93 | // deprecated directly infer your parameters to the constructor 94 | public autoBuildFlags: string; 95 | // legacy path to package.json dir 96 | public rootcwd?: string; 97 | // Path to build all openCV libs 98 | public buildRoot: string; 99 | // Path to find package.json legacy option 100 | public packageRoot: string; 101 | // protected _platform: NodeJS.Platform; 102 | private no_autobuild: string; 103 | 104 | private getExpectedVersion(defaultVersion?: string): string { 105 | if (this.no_autobuild) { 106 | return "0.0.0"; 107 | } 108 | const opencvVersion = this.resolveValue(ALLARGS.version); 109 | if (opencvVersion) { 110 | return opencvVersion; 111 | } 112 | return defaultVersion || ""; 113 | // return '0.0.0'; //DEFAULT_OPENCV_VERSION; 114 | } 115 | 116 | // private getExpectedBuildWithCuda(): boolean { 117 | // return !!this.resolveValue(ALLARGS.cuda); 118 | // } 119 | // this.autoBuildFlags = this.resolveValue(ALLARGS.flags); 120 | // this.#cudaArch = this.resolveValue(ALLARGS.cudaArch); 121 | // this.isWithoutContrib = !!this.resolveValue(ALLARGS.nocontrib); 122 | // this.isAutoBuildDisabled = !!this.resolveValue(ALLARGS.nobuild); 123 | // this.keepsources = !!this.resolveValue(ALLARGS.keepsources); 124 | // this.dryRun = !!this.resolveValue(ALLARGS['dry-run']); 125 | // this.gitCache = !!this.resolveValue(ALLARGS['git-cache']); 126 | 127 | private resolveValue(info: ArgInfo): string { 128 | let value = ""; 129 | if (info.conf in this.opts) { 130 | value = this.opts[info.conf] as string || ""; 131 | } else { 132 | if (this.#packageEnv && this.#packageEnv[info.conf]) { 133 | value = this.#packageEnv[info.conf] || ""; 134 | } else { 135 | value = getEnv(info.env) || ""; 136 | } 137 | } 138 | if (info.isBool) { 139 | return toBool(value) ? "1" : ""; 140 | } else { 141 | return value; 142 | } 143 | } 144 | #packageEnv: OpenCVPackageBuildOptions = {}; 145 | 146 | constructor(private opts = {} as OpenCVBuildEnvParams) { 147 | this.prebuild = opts.prebuild; 148 | this.packageRoot = opts.rootcwd || getEnv("INIT_CWD") || Deno.cwd(); // process.cwd(); 149 | this.buildRoot = StaticTools.getBuildDir(opts); 150 | // get project Root path to looks for package.json for opencv4nodejs section 151 | try { 152 | const data = StaticTools.readEnvsFromPackageJson(); 153 | if (data === null && !this.prebuild) { 154 | Log.log( 155 | "info", 156 | "config", 157 | `No file ${highlight("%s")} found for opencv4nodejs import`, 158 | StaticTools.getPackageJson(), 159 | ); 160 | } 161 | if (data) { 162 | this.#packageEnv = data; 163 | } 164 | } catch (err) { 165 | Log.log( 166 | "error", 167 | "applyEnvsFromPackageJson", 168 | "failed to parse package.json:", 169 | ); 170 | if (err instanceof Error) { 171 | Log.log("error", "applyEnvsFromPackageJson", err.toString()); 172 | } else { 173 | Log.log( 174 | "error", 175 | "applyEnvsFromPackageJson", 176 | JSON.stringify(err), 177 | ); 178 | } 179 | } 180 | // try to use previouse build 181 | this.no_autobuild = toBool(this.resolveValue(ALLARGS.nobuild)) ? "1" : ""; 182 | 183 | if (!this.no_autobuild && opts.prebuild) { 184 | const builds = StaticTools.listBuild(this.rootDir); 185 | if (!builds.length) { 186 | throw Error( 187 | `No build found in ${this.rootDir} you should launch opencv-build-npm once`, 188 | ); 189 | } 190 | const expVer = this.getExpectedVersion("0.0.0"); 191 | /** 192 | * try to match the expected version 193 | */ 194 | let buildV = builds; 195 | if (expVer != "0.0.0") { 196 | buildV = buildV.filter((b) => b.buildInfo.opencvVersion === expVer); 197 | } 198 | /** 199 | * but if no match, use the latest build with a different version number. 200 | */ 201 | // if (buildV.length) 202 | // builds = buildV; 203 | if (!buildV.length) { 204 | throw Error( 205 | `No build of version ${expVer} found in ${this.rootDir} you should launch opencv-build-npm Available versions are: ${ 206 | builds.map((b) => b.buildInfo.opencvVersion).join(", ") 207 | }`, 208 | ); 209 | } 210 | if (buildV.length > 1) { 211 | switch (opts.prebuild) { 212 | case "latestBuild": 213 | builds.sort((a, b) => b.date.getTime() - a.date.getTime()); 214 | break; 215 | case "latestVersion": 216 | builds.sort((a, b) => b.dir.localeCompare(a.dir)); 217 | break; 218 | case "oldestBuild": 219 | builds.sort((a, b) => a.date.getTime() - b.date.getTime()); 220 | break; 221 | case "oldestVersion": 222 | builds.sort((a, b) => a.dir.localeCompare(b.dir)); 223 | break; 224 | } 225 | } 226 | // load envthe prevuious build 227 | const autoBuildFile = builds[0].buildInfo; 228 | //const autoBuildFile = OpenCVBuildEnv.readAutoBuildFile(builds[0].autobuild); 229 | //if (!autoBuildFile) 230 | // throw Error(`failed to read build info from ${builds[0].autobuild}`); 231 | const flagStr = autoBuildFile.env.autoBuildFlags; 232 | this.hash = builds[0].hash; 233 | // merge -DBUILD_opencv_ to internal BUILD_opencv_ manager 234 | if (flagStr) { 235 | const flags = flagStr.split(/\s+/); 236 | flags.filter((flag) => { 237 | if (flag.startsWith("-DBUILD_opencv_")) { 238 | // eslint-disable-next-line prefer-const 239 | let [mod, activated] = flag.substring(15).split("="); 240 | activated = activated.toUpperCase(); 241 | if (activated === "ON" || activated === "1") { 242 | this.#enabledModules.add(mod as OpencvModulesType); 243 | } else if (activated === "OFF" || activated === "0") { 244 | this.#enabledModules.delete(mod as OpencvModulesType); 245 | } 246 | return false; 247 | } 248 | return true; 249 | }); 250 | } 251 | this.autoBuildFlags = flagStr; 252 | this.buildWithCuda = autoBuildFile.env.buildWithCuda; 253 | this.isAutoBuildDisabled = autoBuildFile.env.isAutoBuildDisabled; 254 | this.isWithoutContrib = autoBuildFile.env.isWithoutContrib; 255 | this.opencvVersion = autoBuildFile.env.opencvVersion; 256 | this.buildRoot = autoBuildFile.env.buildRoot; 257 | 258 | Log.log( 259 | "debug", 260 | "OpenCVBuildEnv", 261 | `autoBuildFlags=${highlight(this.autoBuildFlags)}`, 262 | ); 263 | Log.log( 264 | "debug", 265 | "OpenCVBuildEnv", 266 | `buildWithCuda=${highlight("" + (!!this.buildWithCuda))}`, 267 | ); 268 | Log.log( 269 | "debug", 270 | "OpenCVBuildEnv", 271 | `isAutoBuildDisabled=${highlight("" + (this.isAutoBuildDisabled))}`, 272 | ); 273 | Log.log( 274 | "debug", 275 | "OpenCVBuildEnv", 276 | `isWithoutContrib=${highlight("" + (!!this.isWithoutContrib))}`, 277 | ); 278 | Log.log( 279 | "debug", 280 | "OpenCVBuildEnv", 281 | `opencvVersion=${highlight(this.opencvVersion)}`, 282 | ); 283 | Log.log( 284 | "debug", 285 | "OpenCVBuildEnv", 286 | `buildRoot=${highlight(this.buildRoot)}`, 287 | ); 288 | 289 | if (!this.opencvVersion) { 290 | throw Error( 291 | `autobuild file is corrupted, opencvVersion is missing in ${ 292 | builds[0].autobuild 293 | }`, 294 | ); 295 | } 296 | setEnv("OPENCV_BIN_DIR", autoBuildFile.env.OPENCV_BIN_DIR); 297 | setEnv("OPENCV_INCLUDE_DIR", autoBuildFile.env.OPENCV_INCLUDE_DIR); 298 | setEnv("OPENCV_LIB_DIR", autoBuildFile.env.OPENCV_LIB_DIR); 299 | 300 | if (this.buildWithCuda && isCudaAvailable()) { 301 | this.#enabledModules.add("cudaarithm"); 302 | this.#enabledModules.add("cudabgsegm"); 303 | this.#enabledModules.add("cudacodec"); 304 | this.#enabledModules.add("cudafeatures2d"); 305 | this.#enabledModules.add("cudafilters"); 306 | this.#enabledModules.add("cudaimgproc"); 307 | // this.#enabledModules.add('cudalegacy'); 308 | this.#enabledModules.add("cudaobjdetect"); 309 | this.#enabledModules.add("cudaoptflow"); 310 | this.#enabledModules.add("cudastereo"); 311 | this.#enabledModules.add("cudawarping"); 312 | } 313 | return; 314 | } 315 | // try to build a new openCV or use a prebuilt one 316 | if (this.no_autobuild) { 317 | this.opencvVersion = "0.0.0"; 318 | Log.log("info", "init", `no_autobuild is set.`); 319 | const changes = StaticTools.autoLocatePrebuild(); 320 | Log.log("info", "init", changes.summery.join("\n")); 321 | } else { 322 | this.opencvVersion = this.getExpectedVersion("4.10.0"); 323 | Log.log( 324 | "info", 325 | "init", 326 | `using openCV verison ${formatNumber(this.opencvVersion)}`, 327 | ); 328 | 329 | if (getEnv("INIT_CWD")) { 330 | Log.log( 331 | "info", 332 | "init", 333 | `${highlight("INIT_CWD")} is defined overwriting root path to ${ 334 | highlight(getEnv("INIT_CWD")) 335 | }`, 336 | ); 337 | } 338 | // ensure that OpenCV workdir exists 339 | if (!fs.existsSync(this.buildRoot)) { 340 | fs.mkdirSync(this.buildRoot); 341 | if (!fs.existsSync(this.buildRoot)) { 342 | throw new Error(`${this.buildRoot} can not be create`); 343 | } 344 | } 345 | } 346 | 347 | // import configuration from package.json 348 | const envKeys = Object.keys(this.#packageEnv); 349 | if (envKeys.length) { 350 | // print all imported variables 351 | Log.log( 352 | "info", 353 | "applyEnvsFromPackageJson", 354 | "the following opencv4nodejs environment variables are set in the package.json:", 355 | ); 356 | envKeys.forEach((key: string) => 357 | Log.log( 358 | "info", 359 | "applyEnvsFromPackageJson", 360 | `${highlight(key)}: ${ 361 | formatNumber( 362 | this.#packageEnv[key as keyof OpenCVPackageBuildOptions] || "", 363 | ) 364 | }`, 365 | ) 366 | ); 367 | } 368 | 369 | this.autoBuildFlags = this.resolveValue(ALLARGS.flags); 370 | this.buildWithCuda = !!this.resolveValue(ALLARGS.cuda); 371 | this.#cudaArch = this.resolveValue(ALLARGS.cudaArch); 372 | this.isWithoutContrib = !!this.resolveValue(ALLARGS.nocontrib); 373 | this.isAutoBuildDisabled = !!this.resolveValue(ALLARGS.nobuild); 374 | this.keepsources = !!this.resolveValue(ALLARGS.keepsources); 375 | this.dryRun = !!this.resolveValue(ALLARGS["dry-run"]); 376 | this.gitCache = !!this.resolveValue(ALLARGS["git-cache"]); 377 | 378 | if (this.buildWithCuda && isCudaAvailable()) { 379 | this.#enabledModules.add("cudaarithm"); 380 | this.#enabledModules.add("cudabgsegm"); 381 | this.#enabledModules.add("cudacodec"); 382 | this.#enabledModules.add("cudafeatures2d"); 383 | this.#enabledModules.add("cudafilters"); 384 | this.#enabledModules.add("cudaimgproc"); 385 | // this.#enabledModules.add('cudalegacy'); 386 | this.#enabledModules.add("cudaobjdetect"); 387 | this.#enabledModules.add("cudaoptflow"); 388 | this.#enabledModules.add("cudastereo"); 389 | this.#enabledModules.add("cudawarping"); 390 | } 391 | } 392 | 393 | #ready = false; 394 | /** 395 | * complet initialisation. 396 | */ 397 | private getReady(): void { 398 | if (this.#ready) { 399 | return; 400 | } 401 | this.#ready = true; 402 | for (const varname of ["binDir", "incDir", "libDir"]) { 403 | const varname2 = varname as "binDir" | "incDir" | "libDir"; 404 | const value = this.resolveValue(ALLARGS[varname2]); 405 | if (value && getEnv(varname) !== value) { 406 | setEnv(ALLARGS[varname2].env, value); 407 | } 408 | } 409 | if (this.no_autobuild) { 410 | // Try autoDetect opencv paths 411 | if ( 412 | !getEnv("OPENCV_BIN_DIR") || !getEnv("OPENCV_LIB_DIR") || 413 | !getEnv("OPENCV_INCLUDE_DIR") 414 | ) { 415 | detector.applyDetect(); 416 | } 417 | 418 | /** 419 | * no autobuild, all OPENCV_PATHS_ENV should be defined 420 | */ 421 | const errors = []; 422 | for (const varname of OPENCV_PATHS_ENV) { 423 | const value = getEnv(varname); 424 | if (!value) { 425 | errors.push( 426 | `${varname} must be define if auto-build is disabled, and autodetection failed`, 427 | ); 428 | continue; 429 | } 430 | let stats: Stats; 431 | try { 432 | stats = fs.statSync(value); 433 | } catch (_e) { 434 | errors.push(`${varname} is set to non existing "${value}"`); 435 | continue; 436 | } 437 | if (!stats.isDirectory()) { 438 | errors.push( 439 | `${varname} is set to "${value}", that should be a directory`, 440 | ); 441 | } 442 | } 443 | if (errors.length) { 444 | throw Error([...errors, ...detector.summery].join("\n")); 445 | } 446 | } 447 | } 448 | 449 | /** default module build list */ 450 | #enabledModules = new Set( 451 | Object.entries(MODEULES_MAP).filter(([, v]) => v).map(([k]) => 452 | k as OpencvModulesType 453 | ), 454 | ); 455 | 456 | public get enabledModules(): OpencvModulesType[] { 457 | return [...this.#enabledModules]; 458 | } 459 | 460 | public enableModule(mod: OpencvModulesType) { 461 | if (this.#ready) { 462 | throw Error( 463 | "No mode modules change can be done after initialisation done.", 464 | ); 465 | } 466 | this.#enabledModules.add(mod); 467 | } 468 | 469 | public disableModule(mod: OpencvModulesType) { 470 | if (this.#ready) { 471 | throw Error( 472 | "No mode modules change can be done after initialisation done.", 473 | ); 474 | } 475 | this.#enabledModules.delete(mod); 476 | } 477 | 478 | /** 479 | * @returns return cmake flags like: -DBUILD_opencv_modules=ON ... 480 | */ 481 | public getCmakeBuildFlags(): string[] { 482 | const out: string[] = []; 483 | for (const mod of ALL_OPENCV_MODULES) { 484 | const value = this.#enabledModules.has(mod) ? "ON" : "OFF"; 485 | if (value === "OFF" && MODEULES_MAP[mod] === null) { 486 | continue; 487 | } 488 | out.push(`-DBUILD_opencv_${mod}=${value}`); 489 | } 490 | return out.sort(); 491 | } 492 | 493 | // if version < 4.5.6 ffmpeg 5 not compatible 494 | // https://stackoverflow.com/questions/71070080/building-opencv-from-source-in-mac-m1 495 | // brew install ffmpeg@4 496 | // brew unlink ffmpeg 497 | // brew link ffmpeg@4 498 | 499 | public getSharedCmakeFlags(): string[] { 500 | const cMakeflags = [ 501 | `-DCMAKE_INSTALL_PREFIX=${this.opencvBuild}`, 502 | "-DCMAKE_BUILD_TYPE=Release", 503 | "-DCMAKE_BUILD_TYPES=Release", 504 | "-DBUILD_EXAMPLES=OFF", // do not build opencv_contrib samples 505 | "-DBUILD_DOCS=OFF", 506 | "-DBUILD_TESTS=OFF", 507 | "-DBUILD_opencv_dnn=ON", // added 28/12/2022 508 | "-DENABLE_FAST_MATH=ON", 509 | "-DBUILD_PERF_TESTS=OFF", 510 | "-DBUILD_JAVA=OFF", 511 | "-DBUILD_ZLIB=OFF", // https://github.com/opencv/opencv/issues/21389 512 | "-DCUDA_NVCC_FLAGS=--expt-relaxed-constexpr", 513 | "-DWITH_VTK=OFF", 514 | ]; 515 | if (!this.isWithoutContrib) { 516 | cMakeflags.push( 517 | "-DOPENCV_ENABLE_NONFREE=ON", 518 | `-DOPENCV_EXTRA_MODULES_PATH=${this.opencvContribModules}`, 519 | ); 520 | } 521 | cMakeflags.push(...this.getConfiguredCmakeFlags()); 522 | return cMakeflags; 523 | // .cMakeflags.push('-DCMAKE_SYSTEM_PROCESSOR=arm64', '-DCMAKE_OSX_ARCHITECTURES=arm64'); 524 | } 525 | 526 | private getConfiguredCmakeFlagsOnce = false; 527 | 528 | public getConfiguredCmakeFlags(): string[] { 529 | const cMakeflags = []; 530 | if (this.buildWithCuda) { 531 | if (isCudaAvailable()) { 532 | // OpenCVBuildEnv.log('info', 'install', 'Adding CUDA flags...'); 533 | // this.enabledModules.delete('cudacodec');// video codec (NVCUVID) is deprecated in cuda 10, so don't add it 534 | cMakeflags.push( 535 | "-DWITH_CUDA=ON", 536 | "-DCUDA_FAST_MATH=ON", /* optional */ 537 | "-DWITH_CUBLAS=ON", /* optional */ 538 | "-DOPENCV_DNN_CUDA=ON", 539 | ); 540 | 541 | this.#enabledModules.add("cudaarithm"); 542 | this.#enabledModules.add("cudabgsegm"); 543 | this.#enabledModules.add("cudacodec"); 544 | this.#enabledModules.add("cudafeatures2d"); 545 | this.#enabledModules.add("cudafilters"); 546 | this.#enabledModules.add("cudaimgproc"); 547 | // this.#enabledModules.add('cudalegacy'); 548 | this.#enabledModules.add("cudaobjdetect"); 549 | this.#enabledModules.add("cudaoptflow"); 550 | this.#enabledModules.add("cudastereo"); 551 | this.#enabledModules.add("cudawarping"); 552 | 553 | const cudaArch = this.cudaArch; 554 | if (cudaArch) { 555 | cMakeflags.push(`-DCUDA_ARCH_BIN=${cudaArch}`); 556 | } 557 | } else { 558 | if (!this.getConfiguredCmakeFlagsOnce) { 559 | Log.log("error", "install", "failed to locate CUDA setup"); 560 | } 561 | } 562 | } 563 | 564 | // add user added flags 565 | if (this.autoBuildFlags && typeof (this.autoBuildFlags) === "string") { 566 | const addedFlags = this.autoBuildFlags.split(/\s+/); 567 | const buildList = addedFlags.find((a) => a.startsWith("-DBUILD_LIST")); 568 | if (buildList) { 569 | if (!this.getConfiguredCmakeFlagsOnce) { 570 | Log.log( 571 | "info", 572 | "config", 573 | `cmake flag contains special ${pc.red("DBUILD_LIST")} options "${ 574 | highlight("%s") 575 | }" automatic cmake flags are now disabled.`, 576 | buildList, 577 | ); 578 | } 579 | const extraModules = (buildList.split("=")[1] || "").split(",").filter( 580 | (a) => a, 581 | ); 582 | for (const extraModule of extraModules) { 583 | // drop any --DWITH_ 584 | ALL_OPENCV_MODULES.delete(extraModule as OpencvModulesType); 585 | // or use --DWITH_modules=ON 586 | // this.#enabledModules.add(extraModule as OpencvModulesType); 587 | } 588 | } else { 589 | cMakeflags.push(...this.getCmakeBuildFlags()); 590 | } 591 | // OpenCVBuildEnv.log('silly', 'install', 'using flags from OPENCV4NODEJS_AUTOBUILD_FLAGS:', this.autoBuildFlags) 592 | // cMakeflags.push(...this.autoBuildFlags.split(/\s+/)); 593 | for (const arg of addedFlags) { 594 | const m = arg.match(/^(-D.+=)(.+)$/); 595 | if (!m) { 596 | cMakeflags.push(arg); 597 | continue; 598 | } 599 | const [, key] = m; 600 | const pos = cMakeflags.findIndex((a) => a.startsWith(key)); 601 | if (pos >= 0) { 602 | if (cMakeflags[pos] === arg) { 603 | if (!this.getConfiguredCmakeFlagsOnce) { 604 | Log.log( 605 | "info", 606 | "config", 607 | `cmake flag "${highlight("%s")}" had no effect.`, 608 | arg, 609 | ); 610 | } 611 | } else { 612 | if (!this.getConfiguredCmakeFlagsOnce) { 613 | Log.log( 614 | "info", 615 | "config", 616 | `replacing cmake flag "${highlight("%s")}" by "${ 617 | highlight("%s") 618 | }"`, 619 | cMakeflags[pos], 620 | m[0], 621 | ); 622 | } 623 | cMakeflags[pos] = m[0]; 624 | } 625 | } else { 626 | if (!this.getConfiguredCmakeFlagsOnce) { 627 | Log.log( 628 | "info", 629 | "config", 630 | `adding cmake flag "${highlight("%s")}"`, 631 | m[0], 632 | ); 633 | } 634 | cMakeflags.push(m[0]); 635 | } 636 | } 637 | } else { 638 | cMakeflags.push(...this.getCmakeBuildFlags()); 639 | } 640 | // console.log(cMakeflags) 641 | this.getConfiguredCmakeFlagsOnce = true; 642 | return cMakeflags; 643 | } 644 | 645 | public dumpEnv(): EnvSummery { 646 | return { 647 | opencvVersion: this.opencvVersion, 648 | buildWithCuda: this.buildWithCuda, 649 | isWithoutContrib: this.isWithoutContrib, 650 | isAutoBuildDisabled: this.isAutoBuildDisabled, 651 | autoBuildFlags: this.autoBuildFlags, 652 | cudaArch: this.cudaArch, 653 | buildRoot: this.buildRoot, 654 | OPENCV_INCLUDE_DIR: getEnv("OPENCV_INCLUDE_DIR"), 655 | OPENCV_LIB_DIR: getEnv("OPENCV_LIB_DIR"), 656 | OPENCV_BIN_DIR: getEnv("OPENCV_BIN_DIR"), 657 | modules: [...this.#enabledModules].sort(), 658 | }; 659 | } 660 | 661 | public numberOfCoresAvailable(): number { 662 | return os.cpus().length; 663 | } 664 | 665 | private hash = ""; 666 | /** 667 | * openCV uniq version prostfix, used to avoid build path colision. 668 | */ 669 | get optHash(): string { 670 | if (this.hash) { 671 | return this.hash; 672 | } 673 | let optArgs = this.getConfiguredCmakeFlags().join(" "); 674 | if (this.buildWithCuda) optArgs += "cuda"; 675 | if (this.isWithoutContrib) optArgs += "noContrib"; 676 | if (optArgs) { 677 | optArgs = "-" + 678 | crypto.createHash("md5").update(optArgs).digest("hex").substring(0, 5); 679 | } 680 | // do not cache the opt hash, it can change during the configuration process. 681 | // it will be fix durring the final serialisation. 682 | // this.hash = optArgs; 683 | return optArgs; 684 | } 685 | 686 | public get rootDir(): string { 687 | return this.buildRoot; 688 | } 689 | public get opencvRoot(): string { 690 | return path.join( 691 | this.rootDir, 692 | `opencv-${this.opencvVersion}${this.optHash}`, 693 | ); 694 | } 695 | 696 | public get opencvGitCache(): string { 697 | return path.join(this.rootDir, "opencvGit"); 698 | } 699 | 700 | public get opencvContribGitCache(): string { 701 | return path.join(this.rootDir, "opencv_contribGit"); 702 | } 703 | 704 | public get opencvSrc(): string { 705 | return path.join(this.opencvRoot, "opencv"); 706 | } 707 | public get opencvContribSrc(): string { 708 | return path.join(this.opencvRoot, "opencv_contrib"); 709 | } 710 | public get opencvContribModules(): string { 711 | return path.join(this.opencvContribSrc, "modules"); 712 | } 713 | public get opencvBuild(): string { 714 | return path.join(this.opencvRoot, "build"); 715 | } 716 | public get opencvInclude(): string { 717 | return path.join(this.opencvBuild, "include"); 718 | } 719 | public get opencv4Include(): string { 720 | this.getReady(); 721 | const candidat = getEnv("OPENCV_INCLUDE_DIR"); 722 | if (candidat) return candidat; 723 | return path.join(this.opencvInclude, "opencv4"); 724 | } 725 | public get opencvIncludeDir(): string { 726 | this.getReady(); 727 | return getEnv("OPENCV_INCLUDE_DIR"); 728 | } 729 | public get opencvLibDir(): string { 730 | this.getReady(); 731 | const candidat = getEnv("OPENCV_LIB_DIR"); 732 | if (candidat) return candidat; 733 | return Platfrm.isWindows 734 | ? path.join(this.opencvBuild, "lib/Release") 735 | : path.join(this.opencvBuild, "lib"); 736 | } 737 | public get opencvBinDir(): string { 738 | this.getReady(); 739 | const candidat = getEnv("OPENCV_BIN_DIR"); 740 | if (candidat) return candidat; 741 | return Platfrm.isWindows 742 | ? path.join(this.opencvBuild, "bin/Release") 743 | : path.join(this.opencvBuild, "bin"); 744 | } 745 | public get autoBuildFile(): string { 746 | return path.join(this.opencvRoot, "auto-build.json"); 747 | } 748 | public get autoBuildLog(): string { 749 | if (Platfrm.isWindows) { 750 | return path.join(this.opencvRoot, "build-cmd.bat"); 751 | } else { 752 | return path.join(this.opencvRoot, "build-cmd.sh"); 753 | } 754 | } 755 | public readAutoBuildFile(): AutoBuildFile | undefined { 756 | return StaticTools.readAutoBuildFile(this.autoBuildFile); 757 | } 758 | } 759 | --------------------------------------------------------------------------------