Emscripten และ npm

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

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

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

Docker

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

Docker Registry มีอิมเมจ Emscripten โดย trzeci ที่ฉันใช้มาอย่างกว้างขวาง

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

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

โดยทั่วไปแล้ว อาร์ติแฟกต์บิลด์ที่สร้างโดย Emscripten (ไฟล์ .js และไฟล์ .wasm) ควรเป็นโมดูล JavaScript อีกรายการหนึ่งและเป็นเนื้อหาอีกรายการเท่านั้น ไฟล์ JavaScript สามารถจัดการโดย Bundler เช่น 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 ในคอนเทนเนอร์จะมิเรอร์กับโปรเจ็กต์จริง ไดเรกทอรีที่มิเรอร์เหล่านี้ เรียกว่า "bind mounts"

มาดู 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 จะทำให้ Shell เข้าสู่โหมด "Fail Fast" หากคำสั่งในสคริปต์แสดงผลข้อผิดพลาด สคริปต์ทั้งหมดจะถูกยกเลิกทันที ซึ่งจะเป็นประโยชน์อย่างยิ่งเนื่องจากเอาต์พุตล่าสุดของสคริปต์จะเป็นข้อความแสดงความสำเร็จหรือข้อผิดพลาดที่ทำให้บิลด์ล้มเหลวเสมอ

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

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

สําหรับเว็บ ฉันส่วนใหญ่แนะนําให้ใช้ -Os

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

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

แฟล็กต่อไปนี้ไม่จำเป็นเสมอไปหรือมีประโยชน์สำหรับจุดประสงค์ในการแก้ไขข้อบกพร่องเท่านั้น

  • -s FILESYSTEM=0 เป็นแฟล็กที่เกี่ยวข้องกับ Emscripten ซึ่งจำลองระบบไฟล์ให้คุณได้เมื่อโค้ด C/C++ ใช้การดำเนินการของระบบไฟล์ โดยจะวิเคราะห์โค้ดที่คอมไพล์เพื่อตัดสินใจว่าจะรวมการจำลองระบบไฟล์ไว้ในโค้ด Glue Code หรือไม่ อย่างไรก็ตาม บางครั้งการวิเคราะห์นี้อาจผิดพลาดได้ และคุณต้องจ่ายค่าโค้ดกาวเพิ่มเติมถึง 70kB ที่ค่อนข้างหนักเพื่อจำลองระบบไฟล์ที่คุณอาจไม่จำเป็นต้องใช้ เมื่อใช้ -s FILESYSTEM=0 คุณจะบังคับให้ Emscripten ไม่รวมรหัสนี้ได้
  • -g4 จะทำให้ Emscripten รวมข้อมูลการแก้ไขข้อบกพร่องใน .wasm และจะส่งไฟล์แผนที่แหล่งที่มาสำหรับโมดูล 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++ เป็นทรัพยากร Dependency

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

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

ติดตั้ง Napa เป็นทรัพยากร Dependency

$ npm install --save napa

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

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

เมื่อคุณเรียกใช้ npm install napa จะเป็นผู้ดูแลการโคลนที่เก็บ libvpx GitHub ลงใน 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 ที่ไหน วัตถุประสงค์ของการแจ้งว่าไม่เหมาะสมของ -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 ใหม่ และหน้าสาธิตจะแสดงค่าคงที่

เครื่องมือสำหรับนักพัฒนาเว็บ
ที่แสดงเวอร์ชัน 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

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

บางครั้งไลบรารีต้องอาศัยเครื่องมือเพิ่มเติมในการสร้าง หากทรัพยากร Dependency เหล่านี้หายไปในสภาพแวดล้อมบิลด์ที่ได้จากอิมเมจ 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 เรียกใช้คำสั่ง Shell ภายในคอนเทนเนอร์ การเปลี่ยนแปลงคำสั่งเหล่านี้ในคอนเทนเนอร์จะกลายเป็นส่วนหนึ่งของอิมเมจ 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 เรียกใช้ Dockerfile ทีละขั้นตอนและกำหนดผลลัพธ์ของแต่ละขั้นตอนเป็นอิมเมจของตนเอง รูปภาพระดับกลางเหล่านี้มัก เรียกว่า "เลเยอร์" หากคำสั่งใน 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 และ clone libvpx ด้วยตนเอง เนื่องจากคุณไม่มี Bind mounts เมื่อเรียกใช้ docker build ผลข้างเคียงคือ ไม่จำเป็นต้องใช้ Napa อีกต่อไป