หน้านี้จะกล่าวถึงระบบบิลด์ตามงาน วิธีการทำงาน และ ข้อมูลแทรกที่อาจเกิดขึ้นกับระบบแบบเฉพาะงาน หลังจากสคริปต์ Shell ระบบบิลด์ที่อิงตามงานคือวิวัฒนาการเชิงตรรกะครั้งต่อไปของการสร้าง
การทำความเข้าใจระบบบิลด์ตามงาน
ในระบบการบิลด์แบบอิงตามงาน หน่วยพื้นฐานของงานคืองาน งานแต่ละรายการเป็นสคริปต์ที่ดําเนินการตรรกะประเภทใดก็ได้ และงานจะระบุงานอื่นๆ ว่าเป็นทรัพยากรที่ต้องเรียกใช้ก่อน ระบบบิลด์หลักๆ ส่วนใหญ่ที่ใช้กันในปัจจุบัน เช่น Ant, Maven, Gradle, Grunt และ Rake ทำงานตามงาน แทนที่จะเป็น สคริปต์ Shell ระบบบิลด์ที่ทันสมัยส่วนใหญ่ต้องการวิศวกรในการสร้างไฟล์บิลด์ ที่อธิบายวิธีดำเนินการสร้าง
ลองดูตัวอย่างนี้จากคู่มือ Ant
<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>
Buildfile เขียนใน XML และระบุข้อมูลเมตาง่ายๆ บางอย่างเกี่ยวกับบิลด์
พร้อมด้วยรายการงาน (แท็ก <target>
ใน XML) (Ant ใช้คำว่า target เพื่อแสดงถึง task และใช้คำว่า task เพื่ออ้างอิงถึง commands) แต่ละงานจะประมวลผลรายการคำสั่งที่เป็นไปได้ที่ Ant กำหนดไว้
ซึ่งรวมการสร้างและลบไดเรกทอรี, การเรียกใช้ javac
และ
สร้างไฟล์ JAR คำสั่งชุดนี้สามารถขยายได้โดยผู้ใช้ระบุ
ปลั๊กอินที่ครอบคลุมตรรกะต่างๆ แต่ละงานยังสามารถกําหนดงาน
ขึ้นอยู่กับแอตทริบิวต์ Dependency ความสัมพันธ์เหล่านี้จะสร้างเป็นกราฟที่ไม่มีวงรอบดังที่เห็นในรูปที่ 1
รูปที่ 1 กราฟแบบวนซ้ำแสดงทรัพยากร Dependency
ผู้ใช้จะทำการบิลด์ได้โดยส่งงานไปยังเครื่องมือบรรทัดคำสั่งของ Ant ตัวอย่างเช่น เมื่อผู้ใช้พิมพ์ ant dist
Ant จะทําตามขั้นตอนต่อไปนี้
- โหลดไฟล์ชื่อ
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
เครื่องมือบรรทัดคำสั่ง แล้วเครื่องมือจะกำหนดทุกอย่างที่ต้องเรียกใช้
Ant เป็นซอฟต์แวร์เก่าแก่ที่เปิดตัวครั้งแรกในปี 2000 เครื่องมืออื่นๆ เช่น Maven และ Gradle ได้ปรับปรุง Ant ในช่วงหลายปีที่ผ่านมา และเข้ามาแทนที่ Ant โดยการเพิ่มฟีเจอร์ต่างๆ เช่น การจัดการอย่างอัตโนมัติของข้อกำหนดภายนอกและไวยากรณ์ที่สะอาดขึ้นโดยไม่มี XML แต่ลักษณะของเนื้อหาที่ใหม่กว่า ระบบต่างๆ ยังคงเหมือนเดิม นั่นคืออนุญาตให้วิศวกรเขียนสคริปต์บิลด์ใน ทำงานอย่างมีหลักการและเป็นโมดูล และมอบเครื่องมือเพื่อดำเนินงานเหล่านั้น และจัดการทรัพยากร Dependency ได้
ด้านมืดของระบบบิลด์ตามงาน
เนื่องจากเครื่องมือเหล่านี้โดยพื้นฐานแล้วจะช่วยให้วิศวกรสามารถกำหนดสคริปต์ใดๆ ให้เป็นงานได้ มีประสิทธิภาพอย่างมาก ทำให้คุณทำอะไรได้แทบทุกอย่างที่จินตนาการไว้ ร่วมกัน แต่ความสามารถนี้ก็มีข้อเสียเช่นกัน และระบบบิลด์ตามงานอาจใช้งานยากขึ้นเมื่อสคริปต์บิลด์มีความซับซ้อนมากขึ้น กับระบบดังกล่าวก็คือ ท้ายที่สุดแล้วอุปกรณ์เหล่านี้จะมอบพลังงานที่มากเกินไปให้กับ วิศวกร และทำให้มีพลังงานไม่เพียงพอต่อระบบ เนื่องจากระบบไม่ทราบ สคริปต์กำลังทำอะไรอยู่ ประสิทธิภาพจะลดลง เนื่องจากต้องระมัดระวัง วิธีการทำงานและดำเนินการตามขั้นตอนบิลด์ และระบบไม่สามารถยืนยันได้ว่าสคริปต์แต่ละรายการทํางานตามที่ควรจะเป็น สคริปต์จึงมีแนวโน้มที่จะมีความซับซ้อนมากขึ้นและกลายเป็นอีกสิ่งหนึ่งที่จําเป็นต้องแก้ไขข้อบกพร่อง
ความยากของขั้นตอนบิลด์พร้อมกัน
เวิร์กสเตชันสำหรับการพัฒนาสมัยใหม่มีประสิทธิภาพมากทีเดียว โดยมีแกนประมวลผลหลายแกน สามารถดำเนินการขั้นตอนบิลด์หลายรายการพร้อมกัน แต่ระบบแบบงานมักจะไม่สามารถทำงานหลายอย่างพร้อมกันได้ แม้ว่าดูเหมือนว่าควรจะทำได้ก็ตาม สมมติว่างาน A ขึ้นอยู่กับงาน B และ C เนื่องจากงาน B และ C ไม่มีการพึ่งพากันและกัน สามารถเรียกใช้พร้อมกันได้ หรือไม่ เพื่อให้ระบบรับงาน A ได้อย่างรวดเร็วยิ่งขึ้น บางที ถ้าพวกเขาไม่แตะต้อง ที่มีทรัพยากรเดียวกัน หรืออาจไม่ อาจเป็นเพราะทั้ง 2 รายการใช้ไฟล์เดียวกันในการติดตามสถานะและการเรียกใช้พร้อมกันทำให้เกิดข้อขัดแย้ง ไม่มี ในระบบโดยทั่วไป ซึ่งก็ต้องเสี่ยงกับความขัดแย้งเหล่านี้ (นำไปสู่ปัญหาการสร้างที่พบได้ยากแต่แก้ไขข้อบกพร่องได้ยาก) หรือ จำกัดให้ทั้งบิลด์สามารถทำงานบนเทรดเดียวในกระบวนการเดียว ซึ่งอาจเป็นเรื่องสิ้นเปลืองเปลืองแรงสำหรับนักพัฒนาซอฟต์แวร์ที่มีประสิทธิภาพสูง และ กำหนดความเป็นไปได้ในการกระจายบิลด์ไปยังเครื่องหลายเครื่อง
ปัญหาในการสร้างบิลด์ที่เพิ่มขึ้น
ระบบการสร้างที่ดีทำให้วิศวกรสามารถสร้างงานสร้างเพิ่มขึ้นที่เชื่อถือได้ การเปลี่ยนแปลงเล็กๆ น้อยๆ ที่ไม่จำเป็นต้องสร้างฐานของโค้ดทั้งหมด ขูด ซึ่งมีความสำคัญอย่างยิ่งหากระบบบิลด์ทำงานช้าและไม่สามารถทำงานแบบขนานในขั้นตอนการสร้างด้วยเหตุผลที่กล่าวมาข้างต้น แต่น่าเสียดายที่ระบบบิลด์แบบอิงตามงานก็มีปัญหาเช่นกัน เนื่องจาก Tasks สามารถทำสิ่งต่างๆ ได้มากมาย จึงไม่มีวิธีทั่วไปในการตรวจสอบว่างานเสร็จแล้วหรือยัง งานจํานวนมากจะนําชุดไฟล์ต้นทางมาเรียกใช้คอมไพเลอร์เพื่อสร้างชุดไบนารี ดังนั้นจึงไม่ต้องเรียกใช้ใหม่หากไฟล์ต้นทางพื้นฐานไม่มีการเปลี่ยนแปลง แต่หากไม่มีข้อมูลเพิ่มเติม ระบบก็ไม่สามารถเปิดเผยข้อมูลนี้ได้ งานอาจจะเป็นการดาวน์โหลดไฟล์ที่อาจมีการเปลี่ยนแปลง หรือ เขียนการประทับเวลาที่อาจแตกต่างกันในการเรียกใช้แต่ละครั้ง โดยปกติแล้วระบบต้องเรียกใช้งานทุกรายการอีกครั้งในระหว่างการสร้างแต่ละครั้งเพื่อให้แน่ใจว่าถูกต้อง ระบบบิลด์บางระบบพยายามเปิดใช้บิลด์แบบเพิ่มทีละขั้นโดยอนุญาตให้วิศวกรระบุเงื่อนไขที่จำเป็นต้องเรียกใช้งานอีกครั้ง บางครั้งก็เป็นไปได้ แต่ มักเป็นปัญหาที่ยากกว่าที่เห็น เช่น ในภาษาต่างๆ เช่น C++ ที่ช่วยให้ไฟล์อื่นๆ รวมไฟล์ได้โดยตรง ไม่สามารถพิจารณาไฟล์ทั้งชุดที่ต้องจับตาดูการเปลี่ยนแปลง โดยไม่แยกวิเคราะห์แหล่งที่มาของอินพุต วิศวกรมักจะจบลงด้วยทางลัด และ ทางลัดเหล่านี้อาจทำให้เกิดปัญหาที่ไม่ค่อยพบและน่าหงุดหงิดซึ่งผลการค้นหา นำมาใช้ซ้ำได้ ทั้งๆ ที่ไม่ควรก็ตาม เมื่อเกิดกรณีนี้ขึ้นบ่อยครั้ง วิศวกรจะได้รับ ทำความสะอาดนิสัยก่อนการก่อสร้างแต่ละครั้งเพื่อให้ได้สภาพใหม่ เอาชนะวัตถุประสงค์ของการมีงานสร้างที่เพิ่มขึ้น การหาว่าต้องเรียกใช้งานอีกครั้งเมื่อใดนั้นเป็นเรื่องละเอียดอ่อนมาก และเป็นสิ่งที่เครื่องจักรจัดการได้ดีกว่ามนุษย์
จัดการและแก้ไขข้อบกพร่องของสคริปต์ได้ยาก
สุดท้าย สคริปต์บิลด์ที่ระบบบิลด์แบบอิงตามงานกำหนดมักจะใช้งานยาก แม้ว่าจะมีการตรวจสอบอย่างละเอียดน้อยกว่า แต่ก็ควรสร้างสคริปต์ เป็นโค้ดเช่นเดียวกับที่ระบบสร้างขึ้น และเป็นที่ซ่อนข้อบกพร่องได้ง่าย ต่อไปนี้เป็นตัวอย่างบางส่วนของข้อบกพร่องที่พบได้บ่อยมากเมื่อทำงานกับ ระบบบิลด์ตามงาน:
- งาน ก. ขึ้นอยู่กับงาน ข. เพื่อสร้างไฟล์ที่เฉพาะเจาะจงเป็นเอาต์พุต เจ้าของงาน ข. ไม่ทราบว่ามีงานอื่นๆ ที่ใช้งานนี้ จึงเปลี่ยนงานดังกล่าวให้แสดงผลลัพธ์ในตำแหน่งอื่น จะไม่สามารถตรวจจับได้จนกว่าจะมีคน พยายามเรียกใช้งาน A และพบว่าดำเนินการไม่สำเร็จ
- งาน ก ขึ้นอยู่กับงาน ข ซึ่งขึ้นอยู่กับงาน ค ซึ่งสร้างไฟล์หนึ่งๆ เป็นเอาต์พุตที่งาน ก ต้องการ เจ้าของงาน ข ตัดสินใจว่าไม่จำเป็นต้องใช้งาน C อีกต่อไป ซึ่งทำให้งาน A คือผู้ที่ล้มเหลว แม้ว่างาน B จะไม่สนใจงาน C เลยก็ตาม
- นักพัฒนางานใหม่ทำการคาดเดาเกี่ยวกับเครื่องที่ทำงานอยู่โดยไม่ได้ตั้งใจ เช่น ตำแหน่งของเครื่องมือหรือค่าของตัวแปรสภาพแวดล้อมที่เฉพาะเจาะจง ทำงานในเครื่องได้ แต่กลับไม่สำเร็จ เมื่อใดก็ตามที่นักพัฒนาซอฟต์แวร์รายอื่น ทดลองใช้
- งานมีคอมโพเนนต์ที่ไม่แน่นอน เช่น การดาวน์โหลดไฟล์จากอินเทอร์เน็ตหรือการเพิ่มการประทับเวลาลงในบิลด์ ตอนนี้ผู้ใช้อาจได้ผลลัพธ์ที่แตกต่างกันทุกครั้งที่เรียกใช้บิลด์ ซึ่งหมายความว่าวิศวกรอาจไม่สามารถจำลองและแก้ไขข้อผิดพลาดของกันและกันหรือข้อผิดพลาดที่เกิดขึ้นในระบบบิลด์อัตโนมัติได้เสมอไป
- งานที่มีการพึ่งพาหลายรายการอาจทำให้เกิดเงื่อนไขการแข่งขัน หากงาน ก ขึ้นอยู่กับงาน B และงาน C และงาน B และ C ต่างก็แก้ไข งาน A จะได้รับผลลัพธ์ที่ต่างกัน ขึ้นอยู่กับงาน B และ C เสร็จสิ้นก่อน
เพราะไม่มีวิธีที่ครอบคลุมในการแก้ปัญหาประสิทธิภาพ ความถูกต้อง หรือ ปัญหาด้านการบำรุงรักษาภายในเฟรมเวิร์กแบบอิงตามงานซึ่งระบุไว้ที่นี่ ตราบใดที่วิศวกรสามารถเขียนโค้ดแบบใดก็ได้ที่จะทำงานระหว่างการสร้าง ระบบก็จะมีข้อมูลไม่เพียงพอที่จะเรียกใช้บิลด์ได้อย่างรวดเร็วและถูกต้องเสมอ ในการแก้ปัญหานี้ เราจำเป็นต้องนำอำนาจบางส่วนออกจากมือวิศวกรและส่งต่อให้ระบบ รวมถึงกำหนดบทบาทใหม่ให้กับระบบ ไม่ใช่เป็นงานที่ทำงานอยู่ แต่เป็นการนําเสนอผลงาน
แนวทางนี้นำไปสู่การสร้างระบบบิลด์ที่ใช้อาร์ติแฟกต์อย่างเช่น Blaze และ Bazel