ระบบบิลด์ตามงาน

วันที่ รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

หน้านี้จะกล่าวถึงระบบบิลด์ตามงาน วิธีการทำงาน และ ข้อมูลแทรกที่อาจเกิดขึ้นกับระบบแบบเฉพาะงาน หลังจากสคริปต์ Shell ระบบบิลด์ที่อิงตามงานคือวิวัฒนาการเชิงตรรกะครั้งต่อไปของการสร้าง

การทำความเข้าใจระบบบิลด์ตามงาน

ในระบบบิลด์ตามงาน หน่วยพื้นฐานของงานคืองาน ชิ้น คือสคริปต์ที่สามารถประมวลผลตรรกะชนิดใดก็ได้ และงานที่ระบุอื่นๆ งานเป็นทรัพยากร Dependency ที่ต้องทำงานก่อนหน้า มีการใช้งานระบบบิลด์หลักส่วนใหญ่ ในปัจจุบัน เช่น 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) (มดใช้คำว่า target เพื่อแสดงถึงงาน และใช้คำว่า task เพื่ออ้างถึง ) แต่ละงานจะประมวลผลรายการคำสั่งที่เป็นไปได้ที่ Ant กำหนดไว้ ซึ่งรวมการสร้างและลบไดเรกทอรี, การเรียกใช้ javac และ สร้างไฟล์ JAR คำสั่งชุดนี้สามารถขยายได้โดยผู้ใช้ระบุ ปลั๊กอินที่ครอบคลุมตรรกะต่างๆ แต่ละงานยังสามารถกําหนดงาน ขึ้นอยู่กับแอตทริบิวต์ Dependency ทรัพยากร Dependency เหล่านี้ จะทําให้เกิดกราฟแบบวนซ้ำ ตามที่เห็นในรูปที่ 1

กราฟอะคริลิกแสดงทรัพยากร Dependency

รูปที่ 1 กราฟแบบวนซ้ำแสดงทรัพยากร Dependency

ผู้ใช้จะสร้างบิลด์โดยมอบหมายงานให้กับเครื่องมือบรรทัดคำสั่งของ Ant ตัวอย่างเช่น เมื่อผู้ใช้พิมพ์ ant dist Ant จะดำเนินการขั้นตอนต่อไปนี้

  1. โหลดไฟล์ชื่อ build.xml ในไดเรกทอรีปัจจุบันและแยกวิเคราะห์เป็น สร้างโครงสร้างกราฟที่แสดงในรูปที่ 1
  2. ค้นหางานชื่อ dist ที่ระบุไว้ในบรรทัดคำสั่งและ พบว่าทรัพยากร Dependency กับงานชื่อ compile
  3. ค้นหางานชื่อ compile และพบว่างานดังกล่าวมีการขึ้นต่อกัน งานที่ชื่อ init
  4. ค้นหางานชื่อ init และพบว่าไม่มีทรัพยากร Dependency
  5. เรียกใช้คำสั่งที่ระบุไว้ในงาน init
  6. เรียกใช้คำสั่งที่กำหนดไว้ในงาน compile โดยขึ้นอยู่กับคำสั่งทั้งหมด เรียกใช้ทรัพยากร Dependency ของงานแล้ว
  7. เรียกใช้คำสั่งที่กำหนดไว้ในงาน 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 ในช่วงหลายปีที่ผ่านมา แทนที่ด้วยการเพิ่มฟีเจอร์ เช่น การจัดการบริการภายนอกอัตโนมัติ ทรัพยากร Dependency และไวยากรณ์ที่เป็นระเบียบมากขึ้นโดยไม่ต้องใช้ XML แต่ลักษณะของเนื้อหาที่ใหม่กว่า ระบบต่างๆ ยังคงเหมือนเดิม นั่นคืออนุญาตให้วิศวกรเขียนสคริปต์บิลด์ใน ทำงานอย่างมีหลักการและเป็นโมดูล และมอบเครื่องมือเพื่อดำเนินงานเหล่านั้น และจัดการทรัพยากร Dependency ได้

ด้านมืดของระบบบิลด์ตามงาน

เนื่องจากเครื่องมือเหล่านี้โดยพื้นฐานแล้วจะช่วยให้วิศวกรสามารถกำหนดสคริปต์ใดๆ ให้เป็นงานได้ มีประสิทธิภาพอย่างมาก ทำให้คุณทำอะไรได้แทบทุกอย่างที่จินตนาการไว้ ร่วมกัน แต่ประสิทธิภาพนี้มาพร้อมกับข้อด้อย และระบบการสร้างตามงานสามารถ เริ่มดำเนินการได้ยากเมื่อสคริปต์บิลด์ซับซ้อนขึ้นเรื่อยๆ กับระบบดังกล่าวก็คือ ท้ายที่สุดแล้วอุปกรณ์เหล่านี้จะมอบพลังงานที่มากเกินไปให้กับ วิศวกร 3 คนและมีพลังงานไม่เพียงพอต่อระบบ เนื่องจากระบบไม่ทราบ สคริปต์กำลังทำอะไรอยู่ ประสิทธิภาพจะลดลง เนื่องจากต้องระมัดระวัง วิธีการทำงานและดำเนินการตามขั้นตอนบิลด์ รวมถึงจะไม่มีทางให้ระบบ เพื่อยืนยันว่าแต่ละสคริปต์กำลังทำในสิ่งที่เหมาะสม สคริปต์จึงมีแนวโน้มที่จะเติบโต และกลายเป็นอีกอย่างหนึ่งที่ต้องแก้ไขข้อบกพร่อง

ความยากของขั้นตอนบิลด์พร้อมกัน

เวิร์กสเตชันสำหรับการพัฒนาสมัยใหม่มีประสิทธิภาพมากทีเดียว โดยมีแกนประมวลผลหลายแกน สามารถดำเนินการขั้นตอนบิลด์หลายรายการพร้อมกัน แต่ระบบที่อิงตามงาน จะไม่สามารถทำงานพร้อมกันได้ แม้จะดูเหมือนว่า สามารถทำได้ สมมติว่างาน A ขึ้นอยู่กับงาน B และ C เนื่องจากงาน B และ C ไม่มีการพึ่งพากันและกัน สามารถเรียกใช้พร้อมกันได้ หรือไม่ เพื่อให้ระบบรับงาน A ได้อย่างรวดเร็วยิ่งขึ้น บางที ถ้าพวกเขาไม่แตะต้อง ที่มีทรัพยากรเดียวกัน แต่อาจไม่ใช้ บางทีทั้งสองอาจใช้ไฟล์เดียวกันในการติดตาม สถานะของผู้ใช้ และการเรียกใช้ ในเวลาเดียวกันทำให้เกิดข้อขัดแย้ง ไม่มี ในระบบโดยทั่วไป ซึ่งก็ต้องเสี่ยงกับความขัดแย้งเหล่านี้ (นำไปสู่ปัญหาการสร้างที่พบได้ยากแต่แก้ไขข้อบกพร่องได้ยาก) หรือ จำกัดให้ทั้งบิลด์สามารถทำงานบนเทรดเดียวในกระบวนการเดียว ซึ่งอาจสิ้นเปลืองทรัพยากรมหาศาลไปกับเครื่องสำหรับนักพัฒนาซอฟต์แวร์ กำหนดความเป็นไปได้ในการกระจายบิลด์ไปยังเครื่องหลายเครื่อง

ปัญหาในการสร้างบิลด์ที่เพิ่มขึ้น

ระบบการสร้างที่ดีทำให้วิศวกรสามารถสร้างงานสร้างเพิ่มขึ้นที่เชื่อถือได้ การเปลี่ยนแปลงเล็กๆ น้อยๆ ที่ไม่จำเป็นต้องสร้างฐานของโค้ดทั้งหมด ขูด การดำเนินการนี้มีความสำคัญอย่างยิ่งในกรณีที่ระบบบิลด์ช้าและไม่สามารถ โหลดขั้นตอนบิลด์พร้อมกันด้วยเหตุผลที่ได้กล่าวไปแล้ว แต่น่าเสียดาย ระบบบิลด์ที่อิงตามงานก็มีปัญหาเช่นกัน เพราะงานต่างๆ ทำได้ทุกอย่าง ไม่มีวิธีทั่วไปที่จะตรวจสอบว่าไซต์เหล่านั้นได้ทำเสร็จเรียบร้อยแล้วหรือไม่ หลายงาน เพียงนำชุดไฟล์ต้นฉบับมาใช้ และเรียกใช้คอมไพเลอร์เพื่อสร้างชุด ไบนารี; จึงไม่จำเป็นต้องเรียกใช้อีกครั้งหากไฟล์ต้นฉบับที่สำคัญ ไม่เปลี่ยนแปลง แต่หากไม่มีข้อมูลเพิ่มเติม ระบบก็ไม่สามารถเปิดเผยข้อมูลนี้ได้ งานอาจจะเป็นการดาวน์โหลดไฟล์ที่อาจมีการเปลี่ยนแปลง หรือ เขียนการประทับเวลาที่อาจแตกต่างกันในการเรียกใช้แต่ละครั้ง เพื่อเป็นการรับประกันว่า ระบบมักจะต้องเรียกใช้งานทั้งหมดอีกครั้งระหว่างบิลด์แต่ละรายการ ใช้บ้าง ระบบบิลด์จะพยายามเพิ่มบิลด์โดยให้วิศวกรระบุ ที่จำเป็นต้องเรียกใช้งานใหม่ บางครั้งก็เป็นไปได้ แต่ มักเป็นปัญหาที่ยากกว่าที่เห็น เช่น ในภาษาต่างๆ เช่น C++ ที่ช่วยให้ไฟล์อื่นๆ รวมไฟล์ได้โดยตรง ไม่สามารถพิจารณาไฟล์ทั้งชุดที่ต้องจับตาดูการเปลี่ยนแปลง โดยไม่แยกวิเคราะห์แหล่งที่มาของอินพุต วิศวกรมักจะจบลงด้วยทางลัด และ ทางลัดเหล่านี้อาจทำให้เกิดปัญหาที่ไม่ค่อยพบและน่าหงุดหงิดซึ่งผลการค้นหา นำมาใช้ซ้ำได้ ทั้งๆ ที่ไม่ควรก็ตาม เมื่อเกิดกรณีนี้ขึ้นบ่อยครั้ง วิศวกรจะได้รับ ทำความสะอาดนิสัยก่อนการก่อสร้างแต่ละครั้งเพื่อให้ได้สภาพใหม่ เอาชนะวัตถุประสงค์ของการมีงานสร้างที่เพิ่มขึ้น การพิจารณาว่าเมื่อใดที่จำเป็นต้องทำงานใหม่นั้นเป็นเรื่องที่ซับซ้อนอย่างยิ่ง และ ที่แมชชีนจัดการดีกว่ามนุษย์

ปัญหาการบำรุงรักษาและแก้ไขข้อบกพร่องของสคริปต์

สุดท้าย สคริปต์ของบิลด์ที่กำหนดโดยระบบบิลด์ที่อิงตามงานมักจะ ใช้งานได้ยากมาก แม้ว่าจะมีการตรวจสอบอย่างละเอียดน้อยกว่า แต่ก็ควรสร้างสคริปต์ เป็นโค้ดเช่นเดียวกับที่ระบบสร้างขึ้น และเป็นที่ซ่อนข้อบกพร่องได้ง่าย ต่อไปนี้เป็นตัวอย่างบางส่วนของข้อบกพร่องที่พบได้บ่อยมากเมื่อทำงานกับ ระบบบิลด์ตามงาน:

  • งาน A ขึ้นอยู่กับงาน B ในการสร้างไฟล์หนึ่งๆ เป็นเอาต์พุต เจ้าของ ของงาน B ไม่ทราบว่างานอื่นต้องพึ่งพางานอื่น จึงเปลี่ยนเป็น สร้างเอาต์พุตในตำแหน่งอื่น จะไม่สามารถตรวจจับได้จนกว่าจะมีคน พยายามเรียกใช้งาน A และพบว่าดำเนินการไม่สำเร็จ
  • งาน A ขึ้นอยู่กับงาน B ซึ่งขึ้นอยู่กับงาน C ซึ่งจะสร้าง ไฟล์บางรายการเป็นเอาต์พุตที่งาน ก. ต้องการ เจ้าของงาน ข ตัดสินใจว่าไม่จำเป็นต้องใช้งาน C อีกต่อไป ซึ่งทำให้งาน A คือผู้ที่ล้มเหลว แม้ว่างาน B จะไม่สนใจงาน C เลยก็ตาม
  • นักพัฒนาซอฟต์แวร์ของงานใหม่ตั้งสมมติฐานเกี่ยวกับ ที่กำลังทำงาน เช่น ตำแหน่งของเครื่องมือหรือค่าของ ตัวแปรสภาพแวดล้อมที่เฉพาะเจาะจง ทำงานในเครื่องได้ แต่กลับไม่สำเร็จ เมื่อใดก็ตามที่นักพัฒนาซอฟต์แวร์รายอื่น ทดลองใช้
  • งานมีองค์ประกอบที่ไม่กำหนด เช่น การดาวน์โหลดไฟล์ จากอินเทอร์เน็ตหรือการเพิ่มการประทับเวลาในบิลด์ ปัจจุบัน ผู้คนจะได้รับ ผลลัพธ์อาจต่างกันทุกครั้งที่เรียกใช้บิลด์ ซึ่งหมายความว่า วิศวกรอาจไม่สามารถจำลองและแก้ไขข้อผิดพลาดของกันและกันได้เสมอไป หรือความล้มเหลวที่เกิดขึ้นในระบบบิลด์อัตโนมัติ
  • งานที่มีทรัพยากร Dependency หลายรายการจะสร้างเงื่อนไขการแข่งขันได้ หากงาน ก ขึ้นอยู่กับงาน B และงาน C และงาน B และ C ต่างก็แก้ไข งาน A จะได้รับผลลัพธ์ที่ต่างกัน ขึ้นอยู่กับงาน B และ C เสร็จสิ้นก่อน

ไม่มีวิธีที่ครอบคลุมในการแก้ปัญหาประสิทธิภาพ ความถูกต้อง หรือ ปัญหาด้านการบำรุงรักษาภายในเฟรมเวิร์กแบบอิงตามงานซึ่งระบุไว้ที่นี่ นานมาก เนื่องจากวิศวกรสามารถเขียนโค้ดที่กำหนดเองซึ่งทำงานระหว่างบิลด์ มีข้อมูลไม่เพียงพอที่จะเรียกใช้บิลด์ได้อย่างรวดเร็วเสมอ อย่างถูกต้อง ในการแก้ปัญหา เราต้องดึงศักยภาพของ และจัดให้อยู่ในมือของระบบ และทำให้แนวคิด การทำงานของระบบไม่ใช่งานที่กำลังทำงาน แต่เป็นการสร้างอาร์ติแฟกต์

แนวทางนี้นำไปสู่การสร้างระบบการสร้างที่ใช้อาร์ติแฟกต์อย่างเช่น Blaze และ Bazel