dylib | c type to data type | | Search

The code imports required modules and defines constants, then uses the buildDylib function to compile a dynamic library, extract environment variables, and recompile if necessary, assuming the library is written in C, C++, or Objective-C.

Or in two sentences:

The code imports necessary modules and defines constants for a build process. The buildDylib function compiles a dynamic library, extracts environment variables, and recompiles if necessary, assuming the library is written in C, C++, or Objective-C.

Run example

npm run import -- "build dylib"

build dylib


const fs = require('fs')
const path = require('path')
const { interpret } = require('../Core')
const { safeurl } = importer.import("domain cache tools")
const parse = importer.import("shell parse")

const BUILD_DIRECTORY = path.join(__dirname, '../.build')
const SCAN_ENVIRONMENT = /([A-Z_])\s*[\:-=]+\s*(.*?)\s*(\n|$)/g


function buildDylib(code, pathToCode, ctx) {
  if (!fs.existsSync(BUILD_DIRECTORY)) {
    fs.mkdirSync(BUILD_DIRECTORY)
  }

  const codeCell = interpret(pathToCode)
  // TODO: compare file times for dylib module recompile
  let libName = safeurl(codeCell.questions[0])
  let libPath = path.join(BUILD_DIRECTORY, libName + (codeCell.language == 'cpp' ? '.cpp' : '.c'))
  if (!fs.existsSync(libPath)
    || fs.statSync(codeCell.filename).mtime > fs.statSync(libPath).mtime
  ) {
    fs.writeFileSync(libPath, codeCell.source.join(''))
  }


  let env = {}
  let match;
  while ((match = SCAN_ENVIRONMENT.exec(codeCell.markdown.join(''))) !== null) {
      env[match[1]] = match[2]
  }

  if(codeCell.language == 'cpp') {
    env['CXX'] = parse(process.env.CXX) || ['clang++']
    env['STD'] = parse(process.env.STD) || ['-std=c++17', '-stdlib=libc++']
  } else {
    env['CXX'] = parse(process.env.CXX) || ['clang']
  }

  // TODO: recompile dylib
  let objPath = path.join(BUILD_DIRECTORY, libName + '.o')
  if (!fs.existsSync(objPath)
    || fs.statSync(libPath).mtime > fs.statSync(objPath).mtime
  ) {
    const { spawnSync } = require('child_process')
    
    let mods = []
    if(codeCell.language == 'objective-c') {
      mods = ['-x', 'objective-c', '-fno-objc-arc']
    }

    if(codeCell.source.join('').match('@import')) {
      mods = ['-fmodules'].concat(mods)
    }

    let cflags = []
    if(env['PKG_CONFIG']) {
      let result = spawnSync('pkg-config', ['--cflags'].concat(parse(env['PKG_CONFIG'])))
      cflags = parse(result.stdout.toString())
    }

    let args = ['-c', libPath, '-o', objPath]

    // TODO:
    //export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/opt/homebrew/Cellar/mono/6.12.0.206/lib/pkgconfig"
    spawnSync(env['CXX'][0], env['CXX'].slice(1).concat(mods).concat(cflags).concat(args), { stdio: [0, 1, 2], env })

  }

  let modPath = path.join(BUILD_DIRECTORY, libName + '.dylib')
  if (!fs.existsSync(modPath)
    || fs.statSync(objPath).mtime > fs.statSync(modPath).mtime
  ) {
    const { spawnSync } = require('child_process')

    let libs = []
    if(env['PKG_CONFIG']) {
      let result = spawnSync('pkg-config', ['--libs'].concat(parse(env['PKG_CONFIG'])))
      libs = parse(result.stdout.toString())
    }

    let mods = []
    if(env['CXX'].match('clang')) {
      mods = ['-dynamiclib', '-rdynamic']
    }

    let objs = [objPath]
    let args = ['-o', modPath]

    spawnSync(env['CXX'][0], objs.concat(mods).concat(libs).concat(args), { stdio: [0, 1, 2] })
  }

}

module.exports = buildDylib

What the code could have been:

const path = require('path');
const { interpret, safeurl } = require('./core');
const { parse } = require('./shell-parse');

const BUILD_DIRECTORY = path.join(__dirname, '../.build');
const SCAN_ENVIRONMENT = /([A-Z_])\s*[\:-=]+\s*(.*?)\s*(\n|$)/g;

/**
 * Build a dynamic library from the given code.
 * 
 * @param {object} code - The code to build.
 * @param {string} pathToCode - The path to the code file.
 * @param {object} ctx - The build context.
 */
function buildDylib(code, pathToCode, ctx) {
  // Create the build directory if it does not exist.
  if (!fs.existsSync(BUILD_DIRECTORY)) {
    fs.mkdirSync(BUILD_DIRECTORY);
  }

  // Interpret the code and get the code cell.
  const codeCell = interpret(pathToCode);

  // Get the library name and path.
  const libName = safeurl(codeCell.questions[0]);
  const libPath = path.join(BUILD_DIRECTORY, libName + (codeCell.language == 'cpp'? '.cpp' : '.c'));

  // Check if the library source file needs to be written.
  if (!fs.existsSync(libPath) || fs.statSync(codeCell.filename).mtime > fs.statSync(libPath).mtime) {
    // Write the library source file.
    fs.writeFileSync(libPath, codeCell.source.join(''));
  }

  // Initialize the environment object.
  const env = {};

  // Parse the environment variables from the code cell's markdown.
  let match;
  while ((match = SCAN_ENVIRONMENT.exec(codeCell.markdown.join('')))!== null) {
    env[match[1]] = match[2];
  }

  // Parse the CXX and STD environment variables.
  if (codeCell.language == 'cpp') {
    env['CXX'] = parse(process.env.CXX) || ['clang++'];
    env['STD'] = parse(process.env.STD) || ['-std=c++17', '-stdlib=libc++'];
  } else {
    env['CXX'] = parse(process.env.CXX) || ['clang'];
  }

  // Build the library object file.
  const objPath = path.join(BUILD_DIRECTORY, libName + '.o');
  if (!fs.existsSync(objPath) || fs.statSync(libPath).mtime > fs.statSync(objPath).mtime) {
    // Get the compiler and flags.
    const { spawnSync } = require('child_process');
    let mods = [];
    if (codeCell.language == 'objective-c') {
      mods = ['-x', 'objective-c', '-fno-objc-arc'];
    }

    if (codeCell.source.join('').match('@import')) {
      mods = ['-fmodules'].concat(mods);
    }

    let cflags = [];
    if (env['PKG_CONFIG']) {
      let result = spawnSync('pkg-config', ['--cflags'].concat(parse(env['PKG_CONFIG'])));
      cflags = parse(result.stdout.toString());
    }

    let args = ['-c', libPath, '-o', objPath];

    try {
      spawnSync(env['CXX'][0], env['CXX'].slice(1).concat(mods).concat(cflags).concat(args), { stdio: [0, 1, 2], env });
    } catch (error) {
      console.error(`Error building library object file: ${error}`);
      return;
    }
  }

  // Build the dynamic library.
  const modPath = path.join(BUILD_DIRECTORY, libName + '.dylib');
  if (!fs.existsSync(modPath) || fs.statSync(objPath).mtime > fs.statSync(modPath).mtime) {
    // Get the linker flags.
    let libs = [];
    if (env['PKG_CONFIG']) {
      let result = spawnSync('pkg-config', ['--libs'].concat(parse(env['PKG_CONFIG'])));
      libs = parse(result.stdout.toString());
    }

    let mods = [];
    if (env['CXX'].match('clang')) {
      mods = ['-dynamiclib', '-rdynamic'];
    }

    let objs = [objPath];
    let args = ['-o', modPath];

    try {
      spawnSync(env['CXX'][0], objs.concat(mods).concat(libs).concat(args), { stdio: [0, 1, 2] });
    } catch (error) {
      console.error(`Error building dynamic library: ${error}`);
      return;
    }
  }
}

module.exports = buildDylib;

Code Breakdown

Requires and Constants

The code begins by importing required modules and defining constants:

buildDylib Function

The buildDylib function takes three arguments: code, pathToCode, and ctx. It performs the following operations:

  1. Create Build Directory: If the build directory does not exist, it is created.
  2. Interpret Code: The code is interpreted using the interpret function, and the resulting codeCell object is created.
  3. Determine Library Name and Path: The library name is determined by URL-encoding the first question in the code cell, and the library path is constructed.
  4. Compile Library: If the library file does not exist or is outdated, it is compiled to the library path.
  5. Extract Environment Variables: Environment variables are extracted from the code cell's markdown using the regular expression SCAN_ENVIRONMENT.
  6. Set CXX and STD Variables: Based on the language, CXX and STD variables are set to default values.
  7. Recompile Library: If the library object file does not exist or is outdated, it is recompiled.

Functionality

The code appears to be a part of a build process for a dynamic library (dylib). It compiles the library code, extracts environment variables, and recompiles the library if necessary. The code assumes that the library is written in C, C++, or Objective-C.