Emscripten และ npm

คุณผสานรวม WebAssembly เข้ากับการตั้งค่านี้ได้อย่างไร ในบทความนี้ เราจะใช้ C/C++ และ Emscripten เป็นตัวอย่าง

WebAssembly (WASM) มักถูกมองว่าเป็นพื้นฐานด้านประสิทธิภาพหรือวิธีเรียกใช้ฐานโค้ด C++ ที่มีอยู่บนเว็บ squoosh.app สร้างขึ้นเพื่อแสดงให้เห็นว่า WASM มีมุมมองอย่างน้อย 3 มุมมอง ได้แก่ การใช้ระบบนิเวศขนาดใหญ่ของภาษาโปรแกรมอื่นๆ Emscripten ให้คุณใช้โค้ด C/C++ ได้ Rust มี WASM ในตัว และทีม Go ก็กำลังพัฒนาด้วย และเรามั่นใจว่าจะมีภาษาอื่นๆ ตามมาอีกมากมาย

ในกรณีเหล่านี้ wasm ไม่ใช่หัวใจหลักของแอป แต่เป็นชิ้นส่วนของปริศนาอีกชิ้นหนึ่ง แอปของคุณมี JavaScript, CSS, ชิ้นงานรูปภาพ, ระบบบิลด์ที่เน้นเว็บ และอาจรวมถึงเฟรมเวิร์กอย่าง React อยู่แล้ว คุณผสานรวม WebAssembly เข้ากับการตั้งค่านี้ได้อย่างไร ในบทความนี้ เราจะอธิบายเรื่องนี้โดยใช้ C/C++ และ Emscripten เป็นตัวอย่าง

Docker

เราพบว่า Docker มีประโยชน์อย่างยิ่งเมื่อทำงานกับ Emscripten ไลบรารี C/C++ มักเขียนขึ้นให้ทำงานร่วมกับระบบปฏิบัติการที่ใช้สร้าง การมีสภาพแวดล้อมที่สอดคล้องกันมีประโยชน์อย่างยิ่ง เมื่อใช้ Docker คุณจะได้รับระบบ Linux แบบเสมือนจริงที่ตั้งค่าให้ทำงานร่วมกับ Emscripten ไว้แล้ว รวมถึงมีเครื่องมือและไลบรารีที่ต้องพึ่งพาทั้งหมดติดตั้งไว้แล้ว หากมีบางอย่างขาดหายไป คุณก็แค่ติดตั้งได้โดยไม่ต้องกังวลว่าจะส่งผลต่อเครื่องหรือโปรเจ็กต์อื่นๆ ของคุณอย่างไร หากเกิดข้อผิดพลาด ให้ทิ้งคอนเทนเนอร์และเริ่มใหม่ หากใช้งานได้ 1 ครั้ง คุณจะมั่นใจได้ว่าระบบจะยังคงทำงานต่อไปและได้ผลลัพธ์เหมือนกัน

Docker Registry มีภาพ Emscripten โดย trzeci ที่เราใช้อยู่

การผสานรวมกับ npm

ในกรณีส่วนใหญ่ จุดแรกเข้าของโปรเจ็กต์เว็บคือ npm's package.json ตามธรรมเนียมแล้ว โปรเจ็กต์ส่วนใหญ่จะสร้างด้วย npm install && npm run build ได้

โดยทั่วไปแล้ว คุณควรถือว่าอาร์ติแฟกต์การสร้างที่ Emscripten สร้างขึ้น (ไฟล์ .js และ .wasm) เป็นโมดูล JavaScript และชิ้นงานอีกชิ้นหนึ่ง ไฟล์ JavaScript สามารถจัดการโดยเครื่องมือรวมไฟล์ เช่น webpack หรือ rollup และไฟล์ WASM ควรได้รับการปฏิบัติเช่นเดียวกับชิ้นงานไบนารีขนาดใหญ่อื่นๆ เช่น รูปภาพ

ดังนั้น คุณจึงต้องสร้างอาร์ติแฟกต์การสร้าง Emscripten ก่อนเริ่มกระบวนการสร้าง "ปกติ"

{
    "name": "my-worldchanging-project",
    "scripts": {
    "build:emscripten": "docker run --rm -v $(pwd):/src trzeci/emscripten
./build.sh",
    "build:app": "<the old build command>",
    "build": "npm run build:emscripten && npm run build:app",
    // ...
    },
    // ...
}

งาน build:emscripten ใหม่สามารถเรียกใช้ Emscripten ได้โดยตรง แต่ดังที่ได้กล่าวไว้ก่อนหน้านี้ เราขอแนะนำให้ใช้ Docker เพื่อให้แน่ใจว่าสภาพแวดล้อมการสร้างมีความสอดคล้องกัน

docker run ... trzeci/emscripten ./build.sh บอกให้ Docker สร้างคอนเทนเนอร์ใหม่โดยใช้อิมเมจ trzeci/emscripten และเรียกใช้คําสั่ง ./build.sh build.sh คือสคริปต์เชลล์ที่คุณกำลังจะเขียนต่อ --rm บอกให้ Docker ลบคอนเทนเนอร์หลังจากทำงานเสร็จ วิธีนี้จะช่วยให้คุณไม่ต้องสร้างคอลเล็กชันอิมเมจเครื่องที่ล้าสมัยเมื่อเวลาผ่านไป -v $(pwd):/src หมายความว่าคุณต้องการให้ Docker "มิเรอร์" ไดเรกทอรีปัจจุบัน ($(pwd)) ไปยัง /src ในคอนเทนเนอร์ การเปลี่ยนแปลงใดๆ ที่คุณทำกับไฟล์ในไดเรกทอรี /src ภายในคอนเทนเนอร์จะได้รับการมิเรอร์ไปยังโปรเจ็กต์จริง ไดเรกทอรีที่มิเรอร์เหล่านี้เรียกว่า "การต่อเชื่อมการเชื่อมโยง"

มาดูรายละเอียดของ build.sh กัน

#!/bin/bash

set -e

export OPTIMIZE="-Os"
export LDFLAGS="${OPTIMIZE}"
export CFLAGS="${OPTIMIZE}"
export CXXFLAGS="${OPTIMIZE}"

echo "============================================="
echo "Compiling wasm bindings"
echo "============================================="
(
    # Compile C/C++ code
    emcc \
    ${OPTIMIZE} \
    --bind \
    -s STRICT=1 \
    -s ALLOW_MEMORY_GROWTH=1 \
    -s MALLOC=emmalloc \
    -s MODULARIZE=1 \
    -s EXPORT_ES6=1 \
    -o ./my-module.js \
    src/my-module.cpp

    # Create output folder
    mkdir -p dist
    # Move artifacts
    mv my-module.{js,wasm} dist
)
echo "============================================="
echo "Compiling wasm bindings done"
echo "============================================="

มีหลายเรื่องที่ต้องวิเคราะห์

set -e ใส่เชลล์ไว้ในโหมด "Fail Fast" หากคําสั่งใดในสคริปต์แสดงข้อผิดพลาด ระบบจะยกเลิกสคริปต์ทั้งหมดทันที ซึ่งมีประโยชน์อย่างยิ่งเนื่องจากเอาต์พุตสุดท้ายของสคริปต์จะเป็นข้อความ "สำเร็จ" หรือข้อผิดพลาดที่ทำให้บิลด์ไม่สำเร็จเสมอ

คำสั่ง export ช่วยให้คุณกําหนดค่าตัวแปรสภาพแวดล้อม 2 รายการ ซึ่งช่วยให้คุณส่งพารามิเตอร์บรรทัดคำสั่งเพิ่มเติมไปยังคอมไพเลอร์ C (CFLAGS), คอมไพเลอร์ C++ (CXXFLAGS) และโปรแกรมลิงก์ (LDFLAGS) ได้ โดยทั้ง 3 รายการจะได้รับการตั้งค่าเครื่องมือเพิ่มประสิทธิภาพผ่าน OPTIMIZE เพื่อให้แน่ใจว่าทุกอย่างได้รับการเพิ่มประสิทธิภาพในลักษณะเดียวกัน ค่าที่เป็นไปได้สำหรับตัวแปร OPTIMIZE มี 2 ค่า ดังนี้

  • -O0: ไม่เพิ่มประสิทธิภาพ ไม่มีการกำจัดโค้ดที่ตาย และ Emscripten จะไม่ลดขนาดโค้ด JavaScript ที่สร้างขึ้นด้วย เหมาะสำหรับการแก้ไขข้อบกพร่อง
  • -O3: เพิ่มประสิทธิภาพอย่างจริงจัง
  • -Os: เพิ่มประสิทธิภาพอย่างเต็มรูปแบบโดยให้ขนาดเป็นเกณฑ์รอง
  • -Oz: เพิ่มประสิทธิภาพเพื่อลดขนาดโดยเสียประสิทธิภาพหากจำเป็น

สําหรับเว็บ เราขอแนะนํา -Os ส่วนใหญ่

คำสั่ง emcc มีตัวเลือกมากมายในตัวเอง โปรดทราบว่า emcc ควรเป็น "เครื่องมือเปลี่ยนทดแทนคอมไพเลอร์อย่าง GCC หรือ clang" ดังนั้น Flag ทั้งหมดที่คุณอาจรู้จักจาก GCC ก็มีแนวโน้มที่ emcc จะใช้ด้วยเช่นกัน Flag -s มีลักษณะพิเศษตรงที่ช่วยให้เราสามารถกําหนดค่า Emscripten ได้โดยเฉพาะ ตัวเลือกทั้งหมดที่ใช้ได้อยู่ใน settings.js ของ Emscripten แต่ไฟล์นั้นอาจดูน่าสับสน ต่อไปนี้คือรายการ Flag ของ Emscripten ที่ฉันคิดว่าสำคัญที่สุดสำหรับนักพัฒนาเว็บ

  • --bind enables embind
  • -s STRICT=1 หยุดรองรับตัวเลือกการสร้างที่เลิกใช้งานทั้งหมด วิธีนี้ช่วยให้มั่นใจว่าโค้ดจะคอมไพล์ในลักษณะที่ใช้งานร่วมกันได้ในอนาคต
  • -s ALLOW_MEMORY_GROWTH=1 อนุญาตให้เพิ่มหน่วยความจำโดยอัตโนมัติหากจําเป็น ณ เวลาที่เขียนบทความนี้ Emscripten จะจัดสรรหน่วยความจำ 16 MB ในเบื้องต้น เมื่อโค้ดของคุณจัดสรรพื้นที่หน่วยความจำ ตัวเลือกนี้จะกำหนดว่าการดำเนินการเหล่านี้จะทำให้โมดูล WASM ทั้งหมดทำงานไม่สำเร็จเมื่อหน่วยความจำหมดลง หรืออนุญาตให้โค้ดกาวขยายหน่วยความจำทั้งหมดเพื่อรองรับการจัดสรรหรือไม่
  • -s MALLOC=... เลือกการใช้งาน malloc() ที่จะใช้ emmalloc เป็นmalloc()ขนาดเล็กและรวดเร็วที่ติดตั้งใช้งานสำหรับ Emscripten โดยเฉพาะ อีกทางเลือกหนึ่งคือ dlmalloc ซึ่งเป็นการติดตั้งใช้งาน malloc() อย่างเต็มรูปแบบ คุณจำเป็นต้องเปลี่ยนไปใช้ dlmalloc เฉพาะในกรณีที่คุณจัดสรรออบเจ็กต์ขนาดเล็กจำนวนมากบ่อยครั้งหรือต้องการใช้การแยกชุดข้อความ
  • -s EXPORT_ES6=1 จะเปลี่ยนโค้ด JavaScript เป็นโมดูล ES6 ที่มีการส่งออกเริ่มต้นที่ทำงานร่วมกับเครื่องมือรวมโค้ดได้ และต้องตั้งค่า -s MODULARIZE=1 ด้วย

Flag ต่อไปนี้ไม่จําเป็นเสมอไปหรือมีประโยชน์สําหรับการแก้ไขข้อบกพร่องเท่านั้น

  • -s FILESYSTEM=0 เป็น Flag ที่เกี่ยวข้องกับ Emscripten และความสามารถในการจำลองระบบไฟล์ให้คุณเมื่อโค้ด C/C++ ใช้การดำเนินการของระบบไฟล์ โดยจะทำการวิเคราะห์บางอย่างในโค้ดที่คอมไพล์เพื่อตัดสินใจว่าจะรวมการจําลองไฟล์ระบบไว้ในโค้ดกาวหรือไม่ อย่างไรก็ตาม บางครั้งการวิเคราะห์นี้อาจผิดพลาดและคุณอาจต้องจ่ายเงินเพิ่ม 70 KB สำหรับโค้ดการเชื่อมโยงเพิ่มเติมเพื่อจำลองระบบไฟล์ที่คุณอาจไม่ต้องการ -s FILESYSTEM=0 ช่วยให้คุณสามารถบังคับให้ Emscripten ยกเว้นโค้ดนี้
  • -g4 จะทำให้ Emscripten รวมข้อมูลการแก้ไขข้อบกพร่องไว้ใน .wasm และยังสร้างไฟล์ Source Map สำหรับโมดูล wasm ด้วย อ่านข้อมูลเพิ่มเติมเกี่ยวกับการแก้ไขข้อบกพร่องด้วย Emscripten ได้ในส่วนการแก้ไขข้อบกพร่อง

เท่านี้ก็เรียบร้อย มาทดสอบการตั้งค่านี้กันด้วยการสร้าง my-module.cpp เล็กๆ สักตัว

    #include <emscripten/bind.h>

    using namespace emscripten;

    int say_hello() {
      printf("Hello from your wasm module\n");
      return 0;
    }

    EMSCRIPTEN_BINDINGS(my_module) {
      function("sayHello", &say_hello);
    }

และ index.html

    <!doctype html>
    <title>Emscripten + npm example</title>
    Open the console to see the output from the wasm module.
    <script type="module">
    import wasmModule from "./my-module.js";

    const instance = wasmModule({
      onRuntimeInitialized() {
        instance.sayHello();
      }
    });
    </script>

(นี่คือ gist ที่มีไฟล์ทั้งหมด)

หากต้องการสร้างทุกอย่าง ให้เรียกใช้

$ npm install
$ npm run build
$ npm run serve

ไปที่ localhost:8080 แล้วคุณจะเห็นเอาต์พุตต่อไปนี้ในคอนโซล DevTools

เครื่องมือสำหรับนักพัฒนาซอฟต์แวร์แสดงข้อความที่พิมพ์ผ่าน C++ และ Emscripten

การเพิ่มโค้ด C/C++ เป็นส่วนที่ต้องพึ่งพา

หากต้องการสร้างไลบรารี C/C++ สําหรับเว็บแอป คุณต้องใส่โค้ดของไลบรารีนั้นไว้ในโปรเจ็กต์ คุณสามารถเพิ่มโค้ดลงในที่เก็บข้อมูลของโปรเจ็กต์ด้วยตนเอง หรือจะใช้ npm เพื่อจัดการการพึ่งพาประเภทเหล่านี้ก็ได้เช่นกัน สมมติว่าฉันต้องการใช้ libvpx ในเว็บแอป ไลบรารี libvpx เป็นไลบรารี C++ ที่ใช้เข้ารหัสรูปภาพด้วย VP8 ซึ่งเป็นตัวแปลงรหัสที่ใช้ในไฟล์ .webm อย่างไรก็ตาม libvpx ไม่ได้อยู่ใน npm และไม่มี package.json เราจึงติดตั้งโดยใช้ npm โดยตรงไม่ได้

ทางออกของปัญหานี้ก็คือ napa ซึ่งช่วยให้คุณติดตั้ง URL ของที่เก็บ Git ใดก็ได้เป็นข้อกำหนดไว้ในโฟลเดอร์ node_modules

ติดตั้ง Napa เป็นการอ้างอิง

$ npm install --save napa

และตรวจสอบว่าได้เรียกใช้ napa เป็นสคริปต์การติดตั้ง

{
// ...
"scripts": {
    "install": "napa",
    // ...
},
"napa": {
    "libvpx": "git+https://github.com/webmproject/libvpx"
}
// ...
}

เมื่อคุณเรียกใช้ npm install แล้ว Napa จะดูแลการโคลนที่เก็บ GitHub ของ libvpx ไปยัง node_modules ของคุณโดยใช้ชื่อ libvpx

ตอนนี้คุณขยายสคริปต์การสร้างเพื่อสร้าง libvpx ได้แล้ว libvpx ใช้ configure และ make ในการสร้าง แต่โชคดีที่ Emscripten ช่วยตรวจสอบได้ว่า configure และ make ใช้คอมไพเลอร์ของ Emscripten คำสั่ง Wrapper emconfigure และ emmake มีไว้เพื่อวัตถุประสงค์นี้

# ... above is unchanged ...
echo "============================================="
echo "Compiling libvpx"
echo "============================================="
(
    rm -rf build-vpx || true
    mkdir build-vpx
    cd build-vpx
    emconfigure ../node_modules/libvpx/configure \
    --target=generic-gnu
    emmake make
)
echo "============================================="
echo "Compiling libvpx done"
echo "============================================="

echo "============================================="
echo "Compiling wasm bindings"
echo "============================================="
# ... below is unchanged ...

ไลบรารี C/C++ แบ่งออกเป็น 2 ส่วน ได้แก่ ส่วนหัว (เดิมคือไฟล์ .h หรือ .hpp) ซึ่งกำหนดโครงสร้างข้อมูล คลาส ค่าคงที่ ฯลฯ ที่ไลบรารีแสดง และไลบรารีจริง (เดิมคือไฟล์ .so หรือ .a) หากต้องการใช้ค่าคงที่ VPX_CODEC_ABI_VERSION ของไลบรารีในโค้ด คุณต้องรวมไฟล์ส่วนหัวของไลบรารีโดยใช้คำสั่ง #include ดังนี้

#include "vpxenc.h"
#include <emscripten/bind.h>

int say_hello() {
    printf("Hello from your wasm module with libvpx %d\n", VPX_CODEC_ABI_VERSION);
    return 0;
}

ปัญหาคือคอมไพเลอร์ไม่ทราบว่าตำแหน่งที่จะค้นหา vpxenc.h นั่นคือเหตุผลที่ต้องมี Flag -I ซึ่งจะบอกคอมไพเลอร์ว่าต้องตรวจสอบไดเรกทอรีใดเพื่อหาไฟล์ส่วนหัว นอกจากนี้ คุณยังต้องส่งไฟล์ไลบรารีจริงให้กับคอมไพเลอร์ด้วย โดยทำดังนี้

# ... above is unchanged ...
echo "============================================="
echo "Compiling wasm bindings"
echo "============================================="
(
    # Compile C/C++ code
    emcc \
    ${OPTIMIZE} \
    --bind \
    -s STRICT=1 \
    -s ALLOW_MEMORY_GROWTH=1 \
    -s ASSERTIONS=0 \
    -s MALLOC=emmalloc \
    -s MODULARIZE=1 \
    -s EXPORT_ES6=1 \
    -o ./my-module.js \
    -I ./node_modules/libvpx \
    src/my-module.cpp \
    build-vpx/libvpx.a

# ... below is unchanged ...

หากคุณเรียกใช้ npm run build ตอนนี้ คุณจะเห็นกระบวนการสร้างไฟล์ .js ใหม่และไฟล์ .wasm ใหม่ และหน้าเดโมจะแสดงผลค่าคงที่ดังต่อไปนี้

DevTools ที่แสดงเวอร์ชัน ABI ของ libvpx ที่พิมพ์ผ่าน emscripten

นอกจากนี้ คุณยังอาจเห็นว่ากระบวนการบิลด์ใช้เวลานาน สาเหตุที่การสร้างใช้เวลานานอาจแตกต่างกันไป ในกรณีของ libvpx การดำเนินการจะใช้เวลานานเนื่องจากจะคอมไพล์โปรแกรมเปลี่ยนไฟล์และโปรแกรมถอดรหัสสำหรับทั้ง VP8 และ VP9 ทุกครั้งที่คุณเรียกใช้คำสั่งบิลด์ แม้ว่าไฟล์ต้นฉบับจะไม่มีการเปลี่ยนแปลงก็ตาม แม้แต่การเปลี่ยนแปลงเล็กๆ น้อยๆ ในmy-module.cppก็ใช้เวลานานในการสร้าง คุณควรเก็บอาร์ติแฟกต์การสร้างของ libvpx ไว้เมื่อสร้างเป็นครั้งแรก

วิธีหนึ่งในการทำเช่นนี้คือการใช้ตัวแปรสภาพแวดล้อม

# ... above is unchanged ...
eval $@

echo "============================================="
echo "Compiling libvpx"
echo "============================================="
test -n "$SKIP_LIBVPX" || (
    rm -rf build-vpx || true
    mkdir build-vpx
    cd build-vpx
    emconfigure ../node_modules/libvpx/configure \
    --target=generic-gnu
    emmake make
)
echo "============================================="
echo "Compiling libvpx done"
echo "============================================="
# ... below is unchanged ...

(นี่คือ gist ที่มีไฟล์ทั้งหมด)

คำสั่ง eval ช่วยให้เราสามารถตั้งค่าตัวแปรสภาพแวดล้อมโดยการส่งพารามิเตอร์ไปยังสคริปต์บิลด์ คำสั่ง test จะข้ามการสร้าง libvpx หากมีการตั้งค่า $SKIP_LIBVPX (เป็นค่าใดก็ได้)

ตอนนี้คุณสามารถคอมไพล์โมดูลได้โดยไม่ต้องสร้าง libvpx ขึ้นมาใหม่ โดยทำดังนี้

$ npm run build:emscripten -- SKIP_LIBVPX=1

การปรับแต่งสภาพแวดล้อมการสร้าง

บางครั้งไลบรารีอาจต้องใช้เครื่องมือเพิ่มเติมในการสร้าง หากไม่มีทรัพยากรเหล่านี้ในสภาพแวดล้อมการสร้างที่อิมเมจ Docker มีให้ คุณจะต้องเพิ่มทรัพยากรเหล่านั้นด้วยตนเอง ตัวอย่างเช่น สมมติว่าคุณต้องการสร้างเอกสารประกอบของ libvpx โดยใช้ doxygen ด้วย Doxygen ไม่พร้อมใช้งานภายในคอนเทนเนอร์ Docker แต่คุณติดตั้งได้โดยใช้ apt

หากทำใน build.sh คุณจะต้องดาวน์โหลดและติดตั้ง Doxygen อีกครั้งทุกครั้งที่ต้องการสร้างคลัง ไม่เพียงแต่จะสิ้นเปลืองเท่านั้น แต่ยังทำให้คุณทำงานในโปรเจ็กต์ขณะออฟไลน์ไม่ได้อีกด้วย

ในกรณีนี้ คุณควรสร้างอิมเมจ Docker ของคุณเอง อิมเมจ Docker จะสร้างขึ้นโดยการเขียน Dockerfile ที่อธิบายขั้นตอนการสร้าง Dockerfile ค่อนข้างมีประสิทธิภาพและมีคำสั่งมากมาย แต่ส่วนใหญ่คุณใช้เพียง FROM, RUN และ ADD ก็ได้ ในกรณีนี้

FROM trzeci/emscripten

RUN apt-get update && \
    apt-get install -qqy doxygen

FROM ช่วยให้คุณประกาศอิมเมจ Docker ที่ต้องการใช้เป็นจุดเริ่มต้นได้ เราเลือก trzeci/emscripten เป็นพื้นฐาน ซึ่งเป็นรูปภาพที่คุณใช้มาตลอด RUN ให้คุณสั่งให้ Docker เรียกใช้คำสั่งเชลล์ภายในคอนเทนเนอร์ การเปลี่ยนแปลงใดๆ ที่คำสั่งเหล่านี้ทำกับคอนเทนเนอร์จะเป็นส่วนหนึ่งของอิมเมจ Docker คุณต้องปรับ package.json เล็กน้อยเพื่อให้แน่ใจว่าระบบได้สร้างและทำให้รูปภาพ Docker พร้อมใช้งานแล้วก่อนเรียกใช้ build.sh โดยทำดังนี้

{
    // ...
    "scripts": {
    "build:dockerimage": "docker image inspect -f '.' mydockerimage || docker build -t mydockerimage .",
    "build:emscripten": "docker run --rm -v $(pwd):/src mydockerimage ./build.sh",
    "build": "npm run build:dockerimage && npm run build:emscripten && npm run build:app",
    // ...
    },
    // ...
}

(นี่คือ gist ที่มีไฟล์ทั้งหมด)

ซึ่งจะสร้างอิมเมจ Docker ในกรณีที่ยังไม่ได้สร้างเท่านั้น จากนั้นทุกอย่างจะทำงานเหมือนเดิม แต่ตอนนี้สภาพแวดล้อมการสร้างมีคำสั่ง doxygen ที่ใช้ได้แล้ว ซึ่งจะทำให้มีการรวบรวมเอกสารประกอบของ libvpx ด้วย

บทสรุป

ไม่น่าแปลกใจเลยที่โค้ด C/C++ และ npm จะไม่เข้ากันได้ดีนัก แต่คุณสามารถทำให้ระบบทำงานได้อย่างสะดวกสบายด้วยเครื่องมือเพิ่มเติมและการจัดแยกที่ Docker มีให้ การตั้งค่านี้อาจใช้ไม่ได้กับทุกโปรเจ็กต์ แต่ถือเป็นจุดเริ่มต้นที่ดีที่คุณสามารถปรับให้เหมาะกับความต้องการของคุณได้ หากมีคำแนะนำในการปรับปรุง โปรดแชร์

ภาคผนวก: การใช้เลเยอร์อิมเมจ Docker

อีกวิธีหนึ่งคือรวมปัญหาเหล่านี้ไว้กับ Docker และแนวทางการแคชที่ชาญฉลาดของ Docker Docker จะเรียกใช้ไฟล์ Docker แบบทีละขั้นตอนและกำหนดผลลัพธ์ของแต่ละขั้นตอนเป็นอิมเมจของตนเอง รูปภาพขั้นกลางเหล่านี้มักเรียกว่า "เลเยอร์" หากคำสั่งใน Dockerfile ไม่มีการเปลี่ยนแปลง Docker จะไม่เรียกใช้ขั้นตอนนั้นอีกครั้งเมื่อคุณสร้าง Dockerfile อีกครั้ง แต่จะใช้เลเยอร์จากครั้งที่สร้างรูปภาพครั้งล่าสุดแทน

ก่อนหน้านี้ คุณต้องใช้ความพยายามบางอย่างเพื่อไม่ให้ต้องสร้าง libvpx ใหม่ทุกครั้งที่สร้างแอป แต่ตอนนี้คุณสามารถย้ายวิธีการสร้าง libvpx จาก build.sh ไปยัง Dockerfile เพื่อใช้ประโยชน์จากกลไกการแคชของ Docker แทน

FROM trzeci/emscripten

RUN apt-get update && \
    apt-get install -qqy doxygen git && \
    mkdir -p /opt/libvpx/build && \
    git clone https://github.com/webmproject/libvpx /opt/libvpx/src
RUN cd /opt/libvpx/build && \
    emconfigure ../src/configure --target=generic-gnu && \
    emmake make

(นี่คือ gist ที่มีไฟล์ทั้งหมด)

โปรดทราบว่าคุณต้องติดตั้ง git และโคลน libvpx ด้วยตนเองเนื่องจากคุณไม่มีการมาสก์การเชื่อมโยงเมื่อเรียกใช้ docker build ผลข้างเคียงคือคุณไม่จำเป็นต้องใช้ Napa อีกต่อไป