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 फ़ाइल को 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 स्टेटमेंट की मदद से, कुछ एनवायरमेंट वैरिएबल की वैल्यू तय की जाती हैं. इनकी मदद से, 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>

(यहां एक gist दी गई है, जिसमें सभी फ़ाइलें शामिल हैं.)

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

$ 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 का इस्तेमाल करें. 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 रिपॉज़िटरी को आपके node_modules में libvpx नाम से क्लोन कर देता है.

अब 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 ...

(यहां gist दिया गया है, जिसमें सभी फ़ाइलें शामिल हैं.)

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 लिखकर बनाई जाती हैं. इसमें इमेज बनाने के चरणों के बारे में बताया जाता है. Dockerfiles बहुत काम की होती हैं और इनमें कई कमांड होती हैं. हालांकि, ज़्यादातर मामलों में सिर्फ़ 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 इमेज बन गई हो और उपलब्ध हो. इसके लिए, आपको build.sh में थोड़ा बदलाव करना होगा: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",
    // ...
    },
    // ...
}

(यहां gist दिया गया है, जिसमें सभी फ़ाइलें शामिल हैं.)

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

नतीजा

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

अपेंडिक्स: Docker इमेज लेयर का इस्तेमाल करना

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

(यहां gist दिया गया है, जिसमें सभी फ़ाइलें शामिल हैं.)

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