Emscripten और npm

इस सेटअप में WebAssembly को कैसे इंटिग्रेट किया जाता है? इस लेख में, हम उदाहरण के तौर पर C/C++ और Emscripten का इस्तेमाल करके, इस प्रोसेस को पूरा करेंगे.

WebAssembly (wasm) को अक्सर परफ़ॉर्मेंस प्रिमिटिव या वेब पर अपने मौजूदा C++ कोडबेस को चलाने के तरीके के तौर पर फ़्रेम किया जाता है. squoosh.app की मदद से, हम यह दिखाना चाहते थे कि WASM के लिए कम से कम एक और तरीका है: अन्य प्रोग्रामिंग भाषाओं के बड़े नेटवर्क का इस्तेमाल करना. Emscripten के साथ, C/C++ कोड का इस्तेमाल किया जा सकता है, Rust के पास Wasm के लिए सहायता सुविधा पहले से मौजूद होती है. साथ ही, Go की टीम भी इस पर काम कर रही है. मुझे भरोसा है कि आने वाले समय में कई और भाषाओं में भी यह सुविधा उपलब्ध होगी.

इन स्थितियों में, wasm आपके ऐप्लिकेशन का मुख्य हिस्सा नहीं है, बल्कि यह एक पहेली का एक टुकड़ा है: एक और मॉड्यूल. आपके ऐप्लिकेशन में पहले से ही JavaScript, CSS, इमेज ऐसेट, वेब-केंद्रित बिल्ड सिस्टम, और शायद React जैसा फ़्रेमवर्क भी मौजूद है. इस सेटअप में WebAssembly को कैसे इंटिग्रेट किया जाता है? इस लेख में, हम उदाहरण के तौर पर C/C++ और Emscripten के साथ काम करेंगे.

Docker

मुझे Emscripten के साथ काम करते समय, Docker का इस्तेमाल बहुत मददगार लगा. C/C++ लाइब्रेरी अक्सर उस ऑपरेटिंग सिस्टम के साथ काम करने के लिए लिखी जाती हैं जिस पर वे बनाए जाते हैं. एक जैसे माहौल बनाए रखना बहुत ही मददगार होता है. Docker की मदद से, आपको वर्चुअलाइज़ किया गया Linux सिस्टम मिलता है. यह सिस्टम, Emscripten के साथ काम करने के लिए पहले से सेट अप होता है. साथ ही, इसमें सभी टूल और डिपेंडेंसी पहले से इंस्टॉल होती हैं. अगर कुछ मौजूद नहीं है, तो उसे इंस्टॉल किया जा सकता है. इस बात की चिंता करने की ज़रूरत नहीं है कि इससे आपकी मशीन या अन्य प्रोजेक्ट पर क्या असर पड़ेगा. अगर कोई गड़बड़ी होती है, तो कंटेनर को फेंक दें और फिर से शुरू करें. अगर यह एक बार काम करता है, तो आप भरोसा रखें कि यह काम करता रहेगा और एक जैसे नतीजे देगा.

Docker Registry में, trzeci की एक Emscripten इमेज है. इसे मैं काफ़ी समय से इस्तेमाल कर रहा हूं.

npm के साथ इंटिग्रेशन

ज़्यादातर मामलों में, किसी वेब प्रोजेक्ट का एंट्री पॉइंट, एनपीएम का package.json होता है. नियमों के हिसाब से, ज़्यादातर प्रोजेक्ट npm install && npm run build का इस्तेमाल करके बनाए जा सकते हैं.

आम तौर पर, Emscripten (.js और .wasm फ़ाइल) के बनाए गए बिल्ड आर्टफ़ैक्ट को सिर्फ़ एक और JavaScript मॉड्यूल और किसी अन्य ऐसेट के तौर पर माना जाना चाहिए. JavaScript फ़ाइल को वेबपैक या रोलअप जैसे बंडलर से मैनेज किया जा सकता है. साथ ही, 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, डॉकर को बताता है कि कंटेनर चलने के बाद वह कंटेनर मिटा दे. इस तरह, समय के साथ मशीन इमेज का पुराना कलेक्शन नहीं बनता. -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, शेल को "फ़ेल फ़ास्ट" मोड में डालता है. अगर स्क्रिप्ट में कोई निर्देश गड़बड़ी दिखाता है, तो पूरी स्क्रिप्ट तुरंत बंद हो जाती है. यह काफ़ी मददगार हो सकता है, क्योंकि स्क्रिप्ट का आखिरी आउटपुट हमेशा सफलता का मैसेज या वह गड़बड़ी होगी जिसकी वजह से बिल्ड पूरा नहीं हो पाया.

export स्टेटमेंट की मदद से, कुछ एनवायरमेंट वैरिएबल की वैल्यू तय की जाती हैं. इनकी मदद से, C कमपाइलर (CFLAGS), C++ कमपाइलर (CXXFLAGS), और लिंकर (LDFLAGS) को अतिरिक्त कमांड-लाइन पैरामीटर पास किए जा सकते हैं. इन सभी को OPTIMIZE के ज़रिए ऑप्टिमाइज़र सेटिंग मिलती हैं, ताकि यह पक्का किया जा सके कि सभी चीज़ें एक ही तरह से ऑप्टिमाइज़ की जाएं. OPTIMIZE वैरिएबल के लिए कुछ संभावित वैल्यू ये हैं:

  • -O0: कोई ऑप्टिमाइज़ेशन न करें. इसमें कोई भी डेड कोड नहीं हटाया जाता. साथ ही, Emscripten, उत्सर्जित किए गए JavaScript कोड को छोटा भी नहीं करता. डीबग करने के लिए अच्छा है.
  • -O3: परफ़ॉर्मेंस को बेहतर बनाने के लिए, कैंपेन को ज़्यादा से ज़्यादा ऑप्टिमाइज़ करें.
  • -Os: परफ़ॉर्मेंस और साइज़ को ध्यान में रखते हुए, सेकंडरी मानदंड के तौर पर ज़रूरत के मुताबिक ऑप्टिमाइज़ करें.
  • -Oz: साइज़ को ध्यान में रखते हुए ज़रूरत के हिसाब से परफ़ॉर्मेंस ऑप्टिमाइज़ करें.

वेब के लिए, मैं ज़्यादातर -Os का सुझाव देता/देती हूं.

emcc कमांड के पास अनगिनत विकल्प हैं. ध्यान दें कि emcc को "GCC या clang जैसे कंपाइलर के लिए ड्रॉप-इन रिप्लेसमेंट" माना जाता है. इसलिए, जिन फ़्लैग के बारे में आपको जीसीसी के बारे में पता है, उन सभी को emcc भी लागू करने की संभावना है. -s फ़्लैग खास इसलिए है, क्योंकि इससे हमें Emscripten को खास तौर पर कॉन्फ़िगर करने की अनुमति मिलती है. Emscripten के settings.js में सभी उपलब्ध विकल्प देखे जा सकते हैं. हालांकि, यह फ़ाइल बहुत बड़ी हो सकती है. यहां उन एंस्क्रिप्टन फ़्लैग की सूची दी गई है, जो मुझे लगता है कि वेब डेवलपर के लिए सबसे ज़रूरी हैं:

  • --bind, embind को चालू करता है.
  • बिल्ड के उन सभी विकल्पों के लिए -s STRICT=1 ड्रॉप सपोर्ट जो अब काम नहीं करते. इससे यह पक्का होता है कि आपका कोड, फ़ॉरवर्ड के साथ काम करता है.
  • -s ALLOW_MEMORY_GROWTH=1 की मदद से, ज़रूरत पड़ने पर मेमोरी अपने-आप बढ़ जाती है. लिखते समय, Emscripten, शुरुआत में 16 एमबी मेमोरी देगा. आपका कोड, मेमोरी के हिस्से को असाइन करता है. इस विकल्प से यह तय होता है कि मेमोरी खत्म होने पर, इन ऑपरेशन की वजह से क्या पूरा wasm मॉड्यूल काम करना बंद कर देगा या ग्लू कोड को असाइनमेंट के हिसाब से कुल मेमोरी बढ़ाने की अनुमति होगी.
  • -s MALLOC=... यह चुनता है कि malloc() को किस तरह लागू करना है. emmalloc, Emscripten के लिए खास तौर पर बनाया गया एक छोटा और तेज़ malloc() वर्शन है. इसके अलावा, dlmalloc एक पूरी तरह से डेवलप किया गया malloc() विकल्प है. आपको dlmalloc पर सिर्फ़ तब स्विच करना होगा, जब आपको बहुत सारे छोटे ऑब्जेक्ट बार-बार दिए जा रहे हों या थ्रेडिंग का इस्तेमाल करना हो.
  • -s EXPORT_ES6=1, JavaScript कोड को ES6 मॉड्यूल में बदल देगा और इसे डिफ़ॉल्ट एक्सपोर्ट में बदल देगा, जो किसी भी बंडलर के साथ काम करता है. इसके लिए, -s MODULARIZE=1 को भी सेट करना ज़रूरी है.

नीचे दिए गए फ़्लैग हमेशा ज़रूरी नहीं होते या सिर्फ़ डीबग करने के लिए मददगार होते हैं:

  • -s FILESYSTEM=0 एक फ़्लैग है, जो Emscripten और इसकी क्षमता से जुड़ा है. यह आपके लिए फ़ाइल सिस्टम को एमुलेट करता है, जब आपका C/C++ कोड फ़ाइल सिस्टम के ऑपरेशन का इस्तेमाल करता है. यह इकट्ठा किए गए कोड का कुछ विश्लेषण करके, तय करता है कि ग्लू कोड में फ़ाइल सिस्टम एम्युलेशन को शामिल करना है या नहीं. हालांकि, कभी-कभी इस विश्लेषण में गड़बड़ी हो सकती है. ऐसे में, आपको फ़ाइल सिस्टम के ऐसे इम्यूलेशन के लिए 70 केबी का ज़्यादा ग्लू कोड देना पड़ सकता है जिसकी शायद आपको ज़रूरत न हो. -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>

(यहां सभी फ़ाइलों का खास ब्यौरा दिया गया है.)

सब कुछ बनाने के लिए, यह चलाएं

$ npm install
$ npm run build
$ npm run serve

localhost:8080 पर जाने से आपको DevTools कंसोल में यह आउटपुट दिखेगा:

DevTools में C++ और Emscripten की मदद से प्रिंट किया गया मैसेज.

डिपेंडेंसी के तौर पर C/C++ कोड जोड़ना

अगर आपको अपने वेब ऐप्लिकेशन के लिए C/C++ लाइब्रेरी बनानी है, तो आपको अपने प्रोजेक्ट का हिस्सा बनाने के लिए, इसके कोड की ज़रूरत होगी. अपने प्रोजेक्ट की रिपॉज़िटरी में कोड को मैन्युअल तरीके से जोड़ा जा सकता है. इसके अलावा, इस तरह की डिपेंडेंसी को मैनेज करने के लिए, npm का भी इस्तेमाल किया जा सकता है. मान लीजिए कि मुझे अपने वेब ऐप्लिकेशन में libvpx का इस्तेमाल करना है. libvpx, .webm फ़ाइलों में इस्तेमाल किए जाने वाले कोडेक VP8 की मदद से इमेज को कोड में बदलने के लिए एक C++ लाइब्रेरी है. हालांकि, libvpx, npm पर मौजूद नहीं है और इसमें package.json नहीं है. इसलिए, इसे सीधे npm का इस्तेमाल करके इंस्टॉल नहीं किया जा सकता.

इस समस्या से बचने के लिए, tapa है. nava आपको अपने node_modules फ़ोल्डर में डिपेंडेंसी के तौर पर, किसी भी git डेटा स्टोर करने की जगह के यूआरएल को इंस्टॉल करने की अनुमति देता है.

डिपेंडेंसी के तौर पर napa इंस्टॉल करें:

$ npm install --save napa

और napa को इंस्टॉल स्क्रिप्ट के तौर पर चलाना न भूलें:

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

npm install चलाने पर, naa आपके node_modules में libvpx नाम से libvpx GitHub डेटा स्टोर करने की जगह की क्लोनिंग करता है.

अब libvpx को बनाने के लिए, अपनी बिल्ड स्क्रिप्ट को बड़ा किया जा सकता है. libvpx को बनाने के लिए, configure और make का इस्तेमाल किया जाता है. अच्छी बात यह है कि Emscripten की मदद से यह पक्का किया जा सकता है कि configure और make, Emscripten के कंपाइलर का इस्तेमाल करें. इस काम के लिए, रैपर कमांड 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++ लाइब्रेरी दो हिस्सों में बंटी होती है: हेडर (आम तौर पर .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 फ़ाइल बनती है. साथ ही, डेमो पेज से आपको हमेशा स्टैटिक एलिमेंट मिलता है:

DevTools
एंस्क्रिप्टन की मदद से प्रिंट किए गए libvpx का एबीआई वर्शन दिखा रहा है.

आपको यह भी पता चलेगा कि बिल्ड करने की प्रोसेस में काफ़ी समय लगता है. लंबे समय तक बिल्ड टाइम करने की वजहें अलग-अलग हो सकती हैं. 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 ...

(यहां सभी फ़ाइलों वाली एक जानकारी दी गई है.)

eval कमांड की मदद से, बिल्ड स्क्रिप्ट में पैरामीटर भेजकर एनवायरमेंट वैरिएबल सेट किए जा सकते हैं. अगर $SKIP_LIBVPX को किसी भी वैल्यू पर सेट किया गया है, तो test कमांड libvpx को बिल्ड नहीं करेगा.

अब आप अपना मॉड्यूल कंपाइल कर सकते हैं, लेकिन libvpx को फिर से नहीं बना सकते:

$ npm run build:emscripten -- SKIP_LIBVPX=1

बिल्ड एनवायरमेंट को पसंद के मुताबिक बनाना

कभी-कभी लाइब्रेरी बनाने के लिए अतिरिक्त टूल की ज़रूरत होती है. अगर Docker इमेज से मिले बिल्ड एनवायरमेंट में ये डिपेंडेंसी मौजूद नहीं हैं, तो आपको उन्हें खुद जोड़ना होगा. उदाहरण के लिए, मान लें कि आपको doxygen का इस्तेमाल करके libvpx का भी दस्तावेज़ बनाना है. आपके Docker कंटेनर में Doxygen उपलब्ध नहीं है. हालांकि, इसे apt का इस्तेमाल करके इंस्टॉल किया जा सकता है.

अगर आपको अपने build.sh में ऐसा करना है, तो जितनी बार भी अपनी लाइब्रेरी बनानी है उतनी बार doxygen को फिर से डाउनलोड और इंस्टॉल किया जाएगा. इससे न सिर्फ़ आपको नुकसान होगा, बल्कि ऑफ़लाइन होने पर भी आपके प्रोजेक्ट पर काम करना बंद हो जाएगा.

यहां अपनी 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 इमेज का हिस्सा बन जाते हैं. build.sh को चलाने से पहले, यह पक्का करने के लिए कि आपकी Docker इमेज बन गई है और वह उपलब्ध है, आपको अपने package.json में थोड़ा बदलाव करना होगा:

{
    // ...
    "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",
    // ...
    },
    // ...
}

(यहां सभी फ़ाइलों का खास ब्यौरा दिया गया है.)

इससे आपकी Docker इमेज बन जाएगी, लेकिन सिर्फ़ तब जब वह अब तक न बनी हो. इसके बाद, सब कुछ पहले की तरह काम करता है, लेकिन अब बिल्ड एनवायरमेंट में doxygen कमांड उपलब्ध है. इस वजह से, libvpx का दस्तावेज़ भी तैयार हो जाएगा.

नतीजा

इसमें कोई हैरानी की बात नहीं है कि C/C++ कोड और एनपीएम, स्वाभाविक रूप से सही नहीं हैं, लेकिन कुछ अतिरिक्त टूल और Docker से मिलने वाले आइसोलेशन की मदद से, इसे बहुत आसानी से काम किया जा सकता है. यह सेटअप हर प्रोजेक्ट के लिए काम नहीं करेगा, लेकिन यह एक अच्छी शुरुआत है, जिसे अपनी ज़रूरतों के हिसाब से बदला जा सकता है. अगर आपको इसमें कोई सुधार करना है, तो कृपया शेयर करें.

परिशिष्ट: Docker इमेज लेयर का इस्तेमाल करना

इन समस्याओं को हल करने का एक और तरीका है, Docker और कैश मेमोरी के लिए Docker के स्मार्ट तरीके का इस्तेमाल करना. Docker, Dockerफ़ाइलों को सिलसिलेवार तरीके से एक्ज़ीक्यूट करता है. साथ ही, हर चरण के नतीजे को अपनी इमेज असाइन करता है. इन इंटरमीडिएट इमेज को अक्सर "लेयर" कहा जाता है. अगर Dockerfile में कोई निर्देश नहीं बदला गया है, तो Dockerफ़ाइल का फिर से निर्माण करते समय Docker उस चरण को फिर से नहीं चला पाएगा. इसके बजाय, वह पिछली बार इमेज बनाने के दौरान इस्तेमाल की गई लेयर का फिर से इस्तेमाल करता है.

पहले, आपको अपना ऐप्लिकेशन बनाते समय libvpx को फिर से न बनाने के लिए कुछ प्रयास करना पड़ता था. इसके बजाय आप Docker के कैशिंग मैकेनिज़्म का उपयोग करने के लिए libvpx के लिए निर्देशों को अपने build.sh से Dockerfile में ले जा सकते हैं:

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

(यहां सभी फ़ाइलों का खास ब्यौरा दिया गया है.)

ध्यान दें कि आपको git को मैन्युअल तरीके से इंस्टॉल करना होगा और libvpx को क्लोन करना होगा, क्योंकि docker build को चलाते समय आपके पास बाइंड माउंट नहीं होते. ऐसा इसलिए, क्योंकि अब नापा की ज़रूरत नहीं है.