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 के साथ इंटिग्रेशन

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

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

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

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

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

  • --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, C++ लाइब्रेरी है, जो VP8 की मदद से इमेज को कोड में बदलती है. VP8, .webm फ़ाइलों में इस्तेमाल होने वाला कोडेक है. हालांकि, libvpx npm पर मौजूद नहीं है और इसमें package.json नहीं है. इसलिए, इसे सीधे npm का इस्तेमाल करके इंस्टॉल नहीं किया जा सकता.

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

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

$ npm install --save napa

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

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

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

अब 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 में, emscripten की मदद से प्रिंट किए गए 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 का दस्तावेज़ भी बनाना है. 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 इमेज का हिस्सा बन जाते हैं. 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++ कोड और npm एक साथ काम नहीं करते. हालांकि, Docker के कुछ अतिरिक्त टूल और अलग-अलग कंटेनर में काम करने की सुविधा का इस्तेमाल करके, इन दोनों को आसानी से साथ में काम कराया जा सकता है. यह सेटअप हर प्रोजेक्ट के लिए काम नहीं करेगा. हालांकि, यह एक अच्छा शुरुआती बिंदु है, जिसे अपनी ज़रूरतों के हिसाब से अडजस्ट किया जा सकता है. अगर आपको इसमें कोई सुधार करना है, तो कृपया शेयर करें.

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

इन समस्याओं को हल करने का एक और तरीका है, Docker और कैश मेमोरी के लिए Docker के स्मार्ट तरीके का इस्तेमाल करना. Docker, Dockerfiles को सिलसिलेवार तरीके से लागू करता है और हर चरण के नतीजे को अपनी इमेज असाइन करता है. इन इंटरमीडिएट इमेज को अक्सर "लेयर" कहा जाता है. अगर Dockerfile में कोई कमांड नहीं बदला है, तो Dockerfile को फिर से बनाते समय, 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 को चलाते समय आपके पास बाइंड माउंट नहीं होते. इस वजह से, अब आपको एनएपीए की ज़रूरत नहीं है.