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