หน้านี้จะครอบคลุมระบบงานที่อิงตามงาน วิธีการทํางานของระบบ และความซับซ้อนบางประการที่อาจเกิดขึ้นกับระบบที่อิงตามงาน หลังเขียนสคริปต์เสร็จ ระบบงานที่อิงตามงานเป็นวิวัฒนาการถัดไปของการสร้างอาคาร
ทําความเข้าใจระบบงานที่อิงตามงาน
ในระบบงานที่อิงตามงาน หน่วยพื้นฐานของงานคืองาน แต่ละงานคือสคริปต์ที่เรียกใช้ตรรกะต่างๆ ได้ และงานจะระบุงานอื่นๆ เป็นทรัพยากร Dependency ที่ต้องเรียกใช้ก่อน ระบบงานขนาดใหญ่ที่ปัจจุบันใช้ เช่น Ant, Maven, Gradle, Grunt และ Rake นั้นอิงตามงาน แทนที่จะใช้ระบบสคริปต์เปลือย ระบบการสร้างที่ทันสมัยส่วนใหญ่กําหนดให้วิศวกรต้องสร้างไฟล์บิวด์ที่อธิบายวิธีดําเนินการสร้าง
ดูตัวอย่างจากคู่มือมด
<project name="MyProject" default="dist" basedir=".">
<description>
simple example build file
</description>
<!-- set global properties for this build -->
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init"
description="compile the source">
<!-- Compile the Java code from ${src} into ${build} -->
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile"
description="generate the distribution">
<!-- Create the distribution directory -->
<mkdir dir="${dist}/lib"/>
<!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
<jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
</target>
<target name="clean"
description="clean up">
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
ไฟล์ build เขียนเป็น XML และกําหนดข้อมูลเมตาง่ายๆ เกี่ยวกับบิลด์ รวมถึงรายการงาน (แท็ก <target>
ใน XML) (มดใช้คําว่า target เพื่อแสดงถึง task และจะใช้คําว่า task เพื่ออ้างอิงถึง manmanman) งานแต่ละรายการจะดําเนินรายการคําสั่งที่เป็นไปได้ซึ่ง Ant กําหนด ซึ่งรวมถึงการสร้างและลบไดเรกทอรี การเรียกใช้ javac
และการสร้างไฟล์ JAR ชุดคําสั่งนี้สามารถขยายโดยปลั๊กอินที่ผู้ใช้ให้ไว้ครอบคลุมตรรกะใดก็ได้ แต่ละงานยังกําหนดงานที่ขึ้นอยู่ได้ผ่านทางแอตทริบิวต์ดังกล่าว ทรัพยากร Dependency เหล่านี้ประกอบขึ้นเป็นกราฟแบบวนซ้ํา
ดังที่แสดงในรูปที่ 1
รูปที่ 1 กราฟแบบหมุนเพื่อแสดงทรัพยากร Dependency
ผู้ใช้สร้างบิลด์โดยมอบงานไปยังเครื่องมือบรรทัดคําสั่งของมด ตัวอย่างเช่น เมื่อผู้ใช้พิมพ์ ant dist
มดจะดําเนินการขั้นตอนต่อไปนี้
- โหลดไฟล์ชื่อ
build.xml
ในไดเรกทอรีปัจจุบัน และแยกวิเคราะห์เพื่อสร้างโครงสร้างกราฟที่แสดงในรูปที่ 1 - มองหางานชื่อ
dist
ที่ระบุไว้ในบรรทัดคําสั่ง และพบว่างานมีการพึ่งพางานชื่อcompile
- มองหางานชื่อ
compile
และพบว่ามีการพึ่งพางานชื่อinit
- มองหางานชื่อ
init
และพบว่างานดังกล่าวไม่มีทรัพยากร Dependency - เรียกใช้คําสั่งที่กําหนดไว้ในงาน
init
- เรียกใช้คําสั่งที่กําหนดไว้ในงาน
compile
เมื่อเรียกใช้ทรัพยากร Dependency ทั้งหมดของงานนั้นแล้ว - เรียกใช้คําสั่งที่กําหนดไว้ในงาน
dist
เมื่อเรียกใช้ทรัพยากร Dependency ทั้งหมดของงานนั้นแล้ว
ในท้ายที่สุด โค้ดที่ Ant เรียกใช้เมื่อเรียกใช้ dist
เทียบเท่ากับสคริปต์ Shell ต่อไปนี้
./createTimestamp.sh
mkdir build/
javac src/* -d build/
mkdir -p dist/lib/
jar cf dist/lib/MyProject-$(date --iso-8601).jar build/*
เมื่อไวยากรณ์ถูกตัดออก ไฟล์บิวด์และสคริปต์ของบิวด์ก็จริงไม่ต่างกัน แต่เราได้รับรายได้เพิ่มขึ้นอย่างมากจากการดําเนินการนี้ เราสามารถสร้างไฟล์บิวด์ใหม่ในไดเรกทอรีอื่นๆ แล้วลิงก์เข้าด้วยกันได้ เราสามารถเพิ่มงานใหม่ซึ่งอาศัยงานที่มีอยู่ได้อย่างอิสระและซับซ้อน เราต้องส่งผ่านเฉพาะชื่องานเดี่ยวไปยังเครื่องมือบรรทัดคําสั่ง ant
ซึ่งกําหนดทุกอย่างที่จําเป็นต้องเรียกใช้
มดเป็นซอฟต์แวร์ชิ้นเก่าซึ่งเปิดตัวครั้งแรกในปี 2000 ในปี 2019 เครื่องมืออื่นๆ เช่น Maven และ Gradle ได้พัฒนาบน Ant และได้แทนที่ฟีเจอร์ดังกล่าวด้วยการเพิ่มฟีเจอร์ เช่น การจัดการทรัพยากร Dependency อัตโนมัติและไวยากรณ์ที่สะอาดขึ้นโดยไม่ต้องใช้ XML แต่ลักษณะของระบบใหม่ๆ เหล่านี้ยังคงเหมือนเดิมคือ ช่วยให้วิศวกรเขียนสคริปต์บิลด์ในรูปแบบที่มีหลักการและแยกเป็นส่วนได้ในรูปแบบงาน รวมถึงมอบเครื่องมือสําหรับการดําเนินการเหล่านั้นและจัดการทรัพยากร Dependency ได้
ด้านมืดของระบบงานที่อิงตามงาน
เนื่องจากเครื่องมือเหล่านี้ให้วิศวกรกําหนดสคริปต์ต่างๆ เป็นงานได้ งานจึงมีประสิทธิภาพมาก ซึ่งช่วยให้คุณทําสิ่งต่างๆ ได้มากมายตามที่คุณคิด แต่อํานาจดังกล่าวมาพร้อมกับข้อด้อย และระบบงานที่อิงตามงานอาจเป็นเรื่องยากที่จะใช้งานเมื่อสคริปต์บิวด์เติบโตขึ้น ปัญหาที่เกิดกับระบบดังกล่าวคือสุดท้ายแล้วการได้รับพลังงานมากเกินไปแก่วิศวกรและอํานาจของระบบไม่เพียงพอ เนื่องจากระบบไม่มีแนวคิดว่าสคริปต์จะทําอะไร ประสิทธิภาพจึงลดลงเนื่องจากต้องหาวิธีควบคุมและดําเนินการขั้นตอนการสร้างอย่างรอบคอบ และไม่มีวิธีที่ระบบจะยืนยันว่าสคริปต์แต่ละรายการทําหน้าที่ตามที่ควรทําหรือไม่ สคริปต์จึงอาจซับซ้อนขึ้นและสุดท้ายเป็นปัญหาที่ต้องแก้ไขข้อบกพร่อง
ความยากลําบากในการขจัดขั้นตอนของบิลด์
เวิร์กสเตชันสําหรับการพัฒนาสมัยใหม่นั้นค่อนข้างมีประสิทธิภาพเพราะมีแกนหลายตัวที่ทํางานในขั้นตอนการสร้างหลายตัวพร้อมกันได้ แต่ระบบต่างๆ ที่อิงตามงานก็มักจะไม่สามารถขจัดการดําเนินการต่างๆ ไปด้วยได้แม้ว่าจะดูเป็นไปได้ก็ตาม สมมติว่างาน A ขึ้นอยู่กับงาน B และ C เนื่องจากงาน ข และ ค มีการพึ่งพาซึ่งกันและกัน การเรียกใช้งานเหล่านั้นพร้อมกันอย่างปลอดภัยจึงจะช่วยให้ระบบดําเนินการงาน ก ได้เร็วขึ้นหรือไม่ หรือบางทีเขาอาจไม่ได้ใช้ทรัพยากรเดียวกัน แต่อาจไม่ใช่ ทั้งคู่อาจใช้ไฟล์เดียวกันเพื่อติดตามสถานะและเรียกใช้ไฟล์พร้อมกันเกิดความขัดแย้งขึ้น โดยทั่วไปแล้ว ระบบไม่มีทางทราบได้ ดังนั้นจึงมีความเสี่ยงต่อความขัดแย้งเหล่านี้ (ซึ่งนําไปสู่ปัญหาบิลด์ที่ยากแต่แก้ไขข้อบกพร่องมาก) หรือจําเป็นต้องจํากัดบิลด์ทั้งหมดให้ทํางานในชุดข้อความเดียวในกระบวนการเดียว เนื่องจากอาจเป็นสิ้นเปลืองของเครื่องจักรอันทรงพลัง และยังกําจัดความเป็นไปได้อย่างเต็มตัวในการกระจายบิลด์ของเครื่องหลายๆ เครื่องด้วย
ความยากลําบากในการสร้างบิลด์เพิ่มเติม
ระบบบิวด์ที่ดีช่วยให้วิศวกรสร้างบิลด์ที่เชื่อถือได้มากขึ้นเพราะการเปลี่ยนแปลงเพียงเล็กน้อยไม่จําเป็นต้องสร้างฐานของโค้ดใหม่ทั้งหมด ขั้นตอนนี้สําคัญอย่างยิ่งหากระบบบิลด์ทํางานช้าและไม่อาจจับคู่ขั้นตอนบิลด์ให้เป็นไปตามเหตุผลที่กล่าวข้างต้นได้ แต่น่าเสียดายที่ระบบงานที่อิงตามงานพบปัญหาในการทํางานที่นี่เช่นกัน เพราะงานต่างๆ ทําได้ทุกอย่าง จึงไม่มีวิธีปกติที่จะตรวจสอบว่าได้ดําเนินการแล้วหรือยัง งานจํานวนมากจะใช้ชุดไฟล์ต้นฉบับและเรียกใช้คอมไพเลอร์เพื่อสร้างชุดไบนารี ดังนั้นจึงไม่จําเป็นต้องเรียกใช้อีกครั้งหากไฟล์ต้นฉบับที่จําเป็นไม่มีการเปลี่ยนแปลง แต่หากไม่มีข้อมูลเพิ่มเติม ระบบอาจแน่นอนเรื่องนี้ไม่ได้ เช่น งานอาจดาวน์โหลดไฟล์ที่อาจมีการเปลี่ยนแปลง หรืออาจเขียนการประทับเวลาที่แตกต่างกันไปในการเรียกใช้แต่ละครั้ง โดยปกติแล้วระบบจะต้องทํางานทุกๆ อย่างระหว่างบิลด์แต่ละครั้งเพื่อรับประกันความถูกต้อง ระบบงานสร้างบางระบบพยายามเปิดใช้งานสร้างเพิ่มเติมโดยอนุญาตให้วิศวกรระบุเงื่อนไขที่จะต้องเรียกใช้งานอีกครั้ง บางครั้งก็เป็นไปได้ แต่บ่อยครั้งที่ปัญหานั้นยากกว่าที่เคย เช่น ในภาษาอย่าง C++ ที่ช่วยให้ไฟล์รวมอยู่ในไฟล์อื่นๆ ได้โดยตรง คุณไม่สามารถระบุทั้งชุดของไฟล์ที่ต้องดูเพื่อการเปลี่ยนแปลงโดยไม่ต้องแยกวิเคราะห์แหล่งที่มาของอินพุต วิศวกรมักจะต้องใช้ทางลัด และทางลัดเหล่านี้อาจก่อให้เกิดปัญหาเล็กๆ น้อยๆ ที่น่าหงุดหงิดซึ่งมีการใช้ซ้ําผลงานถึงแม้จะไม่เป็นเช่นนั้นก็ตาม เมื่อเกิดกรณีนี้ขึ้น วิศวกรมักจะทําตามนิสัยการวิ่งก่อนที่จะสร้างสถานะใหม่ทุกครั้ง ซึ่งเป็นการเอาชนะใจความที่มีต่อการสร้างบิลด์ในตอนแรกไปเลย การตรวจสอบว่าจะต้องเรียกใช้งานซ้ําอีกนั้นเป็นเรื่องที่น่าแปลกใจมาก และงานที่จัดการโดยเครื่องดีกว่ามนุษย์
การบํารุงรักษาและแก้ไขข้อบกพร่องของสคริปต์
สุดท้าย สคริปต์บิวด์ที่กําหนดโดยระบบงานที่อิงตามงานมักจะทําได้ยาก แม้ว่ามักจะได้รับการตรวจสอบน้อยลง แต่สคริปต์สําหรับสร้างโค้ดก็เป็นโค้ดแบบเดียวกับที่กําลังสร้างและเป็นข้อบกพร่องที่ซ่อนได้ง่าย ต่อไปนี้คือตัวอย่างข้อบกพร่องที่พบบ่อยที่สุดเมื่อทํางานกับระบบงานที่อิงตามงาน
- งาน A ขึ้นอยู่กับงาน B ในการสร้างไฟล์เฉพาะเป็นเอาต์พุต เจ้าของงาน ข ไม่ทราบว่างานอื่นๆ อาศัยงานนั้น จึงเปลี่ยนให้แสดงเอาต์พุตในสถานที่อื่น ซึ่งจะตรวจจับไม่ได้จนกว่าจะมีคนพยายามทํางาน A และพบว่าล้มเหลว
- งาน A ขึ้นอยู่กับงาน B โดยขึ้นอยู่กับงาน C ซึ่งผลิตไฟล์ที่เจาะจงเป็นเอาต์พุตที่จําเป็นสําหรับงาน A เจ้าของงาน ข ตัดสินใจว่าจะไม่ต้องอาศัยงาน C อีกต่อไป ซึ่งทําให้งาน A ล้มเหลวแม้ว่างาน B จะไม่สนใจงาน C เลย
- นักพัฒนาของงานใหม่จะตั้งสมมติฐานเกี่ยวกับเครื่องที่ทํางานโดยไม่ได้ตั้งใจ เช่น ตําแหน่งของเครื่องมือหรือค่าของตัวแปรสภาพแวดล้อมหนึ่งๆ งานดังกล่าวจะทํางานในเครื่องของตัวเอง แต่จะล้มเหลวทุกครั้งที่นักพัฒนาแอปรายอื่นพยายามทํา
- งานจะมีคอมโพเนนต์ที่ไม่ได้กําหนด เช่น การดาวน์โหลดไฟล์จากอินเทอร์เน็ตหรือเพิ่มการประทับเวลาลงในบิลด์ ในปัจจุบัน ผู้ใช้จะเห็นผลลัพธ์ที่แตกต่างกันในแต่ละครั้งที่เรียกใช้บิลด์ ซึ่งหมายความว่าวิศวกรจะจําลองและล้มเหลวความล้มเหลวของกันและกันที่ระบบสร้างแบบอัตโนมัติไม่ได้
- งานที่มีทรัพยากร Dependency หลายรายการจะสร้างเงื่อนไขการแข่งขันได้ หากงาน A ขึ้นอยู่กับทั้งงาน ข และงาน ค และงาน ข และ ค แก้ไขไฟล์เดียวกัน งาน ก จะได้รับผลลัพธ์ที่แตกต่างกันโดยขึ้นอยู่กับว่างาน ข กับ ค เสร็จแล้วหรือไม่
เราไม่มีวัตถุประสงค์ทั่วไปในการแก้ปัญหาประสิทธิภาพ ความถูกต้อง หรือความคงทนภายในเฟรมเวิร์กตามงานที่ระบุไว้ที่นี่ ตราบใดที่วิศวกรสามารถเขียนโค้ดที่กําหนดเองและทํางานระหว่างบิลด์ ระบบอาจไม่มีข้อมูลเพียงพอที่จะเรียกใช้บิลด์ได้อย่างรวดเร็วและถูกต้อง ในการแก้ปัญหา เราต้องนํากําลังไฟฟ้าออกมาจากวิศวกร และนํามือกลับมาอยู่ในระบบก่อน และเปลี่ยนแนวทางบทบาทของระบบใหม่ ไม่ใช่เป็นงาน แต่เป็นการสร้างอาร์ติแฟกต์
แนวทางนี้นําไปสู่การสร้างระบบบิวด์ที่ใช้วัตถุโบราณ เช่น Blaze และ Bazel