Github Template Project

Template Project เป็นฟีเจอร์นึงที่ผมเพิ่งค้นพบว่า GitHub ทำได้! ซึ่งฟีเจอร์นี้เองก็ตามชื่อเลย มันคือการสร้างโปรเจคขึ้นมาเป็นเทมเพลต เพื่อเวลาที่เราสร้างโปรเจคใหม่เราจะได้ไม่ต้องเริ่มจากศูนย์ครับ เราสามารถที่จะเลือก template ที่เรามีเพื่อให้ github ใส่ไฟล์เริ่มต้นที่เราต้องการเข้าไปในโปรเจคใหม่เลยโดยที่ไม่ต้องเซ็ตอัพใหม่ อย่างเช่นโปรเจคนี้ผมเซ็ตให้ใช้เทมเพลตที่เป็นโปรเจคชื่อ sdl2-gl-project-template

ก็จะได้โปรเจคหน้าตาแบบนี้เลย ไม่ต้องมาเสียเวลาเซ็ตอัพโปรเจคใหม่อีก

ทั้งนี้การใช้ template จะแตกต่างกับการ clone โปรเจคอื่นเข้ามาเป็นโปรเจคตั้งต้นตรงที่โปรเจคใหม่จะไม่มี history ของโปรเจคที่เป็น template ติดมาด้วย ทำให้เราไม่ต้องมานั่งลบ history ออก ส่วนข้อเสียคือถ้าเรามีการแก้ไขเทมเพลตใหม่ เราจะ merge มาที่โปรเจคนี้ไม่ได้ (เพราะไม่ได้มี origin ที่เกี่ยวข้องกัน) แต่ผมว่า ณ.จุดนั้นโปรเจคใหม่เองก็น่าจะแตกต่างกับ template มากอยู่แล้ว ดังนั้นคงไม่ต้องกังวลอะไรครับ

การสร้างโปรเจคเป็นเทมเพลตเองก็ไม่ได้ยากอะไรครับ เราสร้างโปรเจคปรกติขึ้นมา แก้โค๊ด เพิ่มไฟล์อะไรให้เรียบร้อยตามที่ต้องการ จากนั้นก็เลือก template repository โปรเจคนี้ก็จะกลายเป็น template project ไปครับ

ลองดูครับ เหมาะกับคนที่เขียนโปรเจคเล็ก ๆ เยอะ ๆ จะได้ไม่ต้องมาเซ็ตอัพโปรเจคบ่อยๆ

ทั้งนี้เราสามารถที่จะเซ็ต git subproject ของ template ได้ เมื่อเราสร้างโปรเจคใหม่จาก template เราจะได้ subproject ติดมาด้วยครับ

อ้างอิง: GitHub

ทดลองใช้ GitHub Actions

GitHub Actions เป็นฟีเจอร์ด้าน Continuous Integration ของ Github ก็คือ เราสามารถลำดับของการกระทำบางอย่างกับโค๊ดหลังจากที่เกิดเหตุการบางอย่างขึ้นบน repository เช่น

  • มีโค๊ดใหม่ถูก push เข้าไปใน repository
  • มี tag ใหม่ถูกสร้างขึ้น
  • มีคนสร้าง pull request

ซึ่งเมื่อเกิดเหตุการดังกล่าว เราก็อาจจะอยากทำอะไรสักอย่างกับโค๊ดหรือโปรเจค เช่น

  • ทดลอง build โค๊ด เพื่อดูว่าเกิด build error หรือ warning
  • ทดลองรันโปรแกรมทดสอบ หาว่ารันได้อย่างที่ควรจะเป็นหรือไม่ มีพฤติกรรมที่ไม่เหมาะสมเกิดขึ้นหรือเปล่า
  • ทำ static analysis หาปัญหาที่อาจจะมีในโค๊ด
  • ส่งโค๊ดไปรันบนเซิร์ฟเวอร์
  • อะไรก็แล้วแต่เท่าที่จะจิตนาการณ์ได้

การกระทำที่ว่านี้ใน GitHub Actions จะเรียกว่า job ครับ

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

ยกตัวอย่าง workflow #1

เมื่อมีคน push code ขึ้นไปใน main branch ให้ทำการ

  1. คอมไพล์โปรแกรมบน Linux
  2. เตรียมข้อมูลสำหรับใช้รันชุดทดสอบ
  3. รันชุดทดสอบ
  4. ติด tag latest ที่ commit ที่ใหม่ที่สุดใน branch
  5. เตรียม docker image
  6. อัพโหลดโปรแกรมที่ release ของ github
  7. สั่งให้ development server ทำการสร้าง container ใหม่โดยรันโปรแกรมเวอร์ชั่นล่าสุด

ทั้งนี้ workflow สามารถทำงานหลาย ๆ job พร้อม ๆ กัน แต่เราสามารถกำหนดว่า job ไหนจะต้องทำหลังจาก job ไหน อย่างเช่นบน workflow ข้างบน ผมอาจจะทำ job #1 กับ #2 พร้อมกันได้ แต่ job #3 จะต้องรอ #1 กับ #2 ให้เสร็จก่อน เราก็กำหนด dependency ไปว่า #3 depend on #1 และ #2 เป็นต้น

แต่ละ job ใน workflow เราสามารถใช้ job ที่มีผู้สร้างเอาไว้อยู่แล้ว หรือกำหนดลำดับการทำงานของแต่ละ job ได้ด้วย เช่นการรันชุดทดสอบผมอาจจะกำหนด job นี้ว่า

  1. สร้าง environment variable จำวนหนึ่ง
  2. ติดโปรแกรม database server ใน virtual environment
  3. รัน database server ใน VE
  4. import test data ที่ใช้ทดสอบเข้าไป
  5. รันชุดทดสอบ
  6. อัพโหลดผลการทดสอบขึ้นไปบน server

ทั้งนี้ในหนึ่งโปรเจคอาจจะมีหลาย workflow ได้ เช่นอาจจะมี workflow หนึ่งสำหรับสร้าง Windows Build อีกตัวหนึ่งสำหรับสร้าง MacOS Build อะไรแบบนี้ GitHub สามารถรันหลาย ๆ workflow พร้อม ๆ กันได้ เพื่อลดเวลาที่ใช้โดยรวมครับ ตัว workflow นี่เราจะเขียนด้วยภาษา yaml ซึ่งจะต้องอยู่ใน directory .github/workflows ภายในโปรเจคเรา ข้างล่างเป็น workflow นึงที่ผมเขียนใช้กับโปรเจคที่ผมเขียนเล่น ๆ อยู่ตอนนี้ครับ

name: main
on: 
  push:
    branches:
      - main

jobs:
  build:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true
      - run: "Get-ChildItem Env:"
      - name: Create output dir
        run: mkdir out

      - name: Configure the project.
        run: cmake -D CMAKE_TOOLCHAIN_FILE=$env:VCPKG_INSTALLATION_ROOT\scripts\buildsystems\vcpkg.cmake $env:GITHUB_WORKSPACE
        working-directory: out

      - name: Build the project.
        run: cmake --build out --config Release

      - name: Copy font files.
        run: xcopy fonts out\Release\fonts\*

      - uses: EndBug/latest-tag@latest

      - uses: actions/upload-artifact@v2
        with:
          name: windows
          path: out/Release

      - name: "Create latest release file"
        run: |
          Get-ChildItem -Path out\Release | Compress-Archive -DestinationPath windows-latest.zip
      - name: Release
        uses: softprops/action-gh-release@v1
        with:
          body: |
            This is the latest, the bleeding edge. Might not work properly and might harm your system. Use with care.
          files: windows-latest.zip
          name: Latest
          prerelease: true
          tag_name: latest
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

จุดเด่นอันหนึ่งของ GitHub Actions คือตัว job จะรันบน Virtual Environment ที่รัน OS ได้หลายแบบ (มี Windows, Linux และ MacOS ให้เลือกในตอนนี้) การที่่สามารถเลือก OS ได้นั้นทำให้เราสามารถที่จะสร้าง job ที่จำเป็นจะต้องใช้ OS เฉพาะทางเท่านั้นถึงจะทำได้ เช่นการ build Mac OS application หรือการทดสอบ web application บน windows server เป็นต้น ทำให้เรามีความสะดวกในการทำงานแบบหลายแพลตฟอร์ม ไม่จำเป็นจะต้องหาเครื่อง host มาทำงานดังกล่าวเอง

อ่านมาจนถึงตรงนี้แล้วหลายคนคงสงสัยว่า ฟีเจอร์เทพขนาดนี้ แล้วราคามันจะแพงหรือไม่ สำหรับโปรเจคที่เป็น Open Source เราสามารถใช้งาน GitHub Actions ได้ฟรีครับ ส่วนพวก Private Repository จะมีค่าใช้จ่าย (แพงอยู่เหมือนกัน) อันนี้ลองศึกษาหาข้อมูลเพิ่มดูครับ

ส่วนความไม่สะดวกของ GitHub Actions ตอนนี้น่าจะเป็นเรื่องที่เราไม่สามารถทดสอบตัว workflow ในเครื่องเราเองได้ ดังนั้นกว่าที่จะมี workflow ที่ใช้งานได้จริงก็อาจจะต้องรันบน server หลายครั้งอยู่ครับ อันนี้จะไม่สะดวกเท่าของ CircleCI ที่สามารถทดสอบในเครื่องตัวเองได้เลย

สำหรับคนที่สนใจลองศึกษาเพิ่มบนเว็บของ GitHub ดูครับ

VCPKG ระบบจัดการแพคเกจ จากทีม Visual C++

หลาย ๆ คนที่เคยใช้ Linux เนี่ย ไม่ว่าจะเป็น Debian, Redhat หรือ Arch จะคุ้นเคยกับการใช้ระบบ package management system ในการติดตั้ง software ต่าง ๆ รวมทั้งการติดั้ง library สำหรับใช้ในโปรเจคภาษา C หรือ C++ ด้วย ซึ่งมันติดตั้งได้ง่ายมาก แค่คำสั่งหนึ่งบรรทัดมันก็ติดตั้งได้ละ

ในหลายภาษาเองก็มีการจัดการ package สำหรับ library เป็นของตัวเอง อย่าง go get หรือ npm install เป็นต้น

แต่ ใน C++ นั้นไม่มีวิธีการจัดการในตัวมันเอง ยิ่งถ้าเราใช้ระบบปฎิบัติการอย่าง Windows ที่ก็ไม่มีระบบจัดการแพคเกจ (อย่างเป็นทางการ) เป็นของตัวเอง ก็จะยิ่งเหนื่อยหน่อย เวลาเซ็ตอัพโปรเจคทีก็ต้องมานั่งดาวน์โหลด Library มาเซ็ต path และอื่น ๆ ก็รู้สึกว่าน่าเบื่อใช่ไหมครับ

วันนี้เราขอเสนอ หนึ่งในทางเลือก package management ของ C++ ที่ชื่อว่า VCPKG

VCPKG จาก Microsoft

VCPKG เป็นตัวจัดการแพคเกจจาก Microsoft ที่ทำงานได้ทั้งบน Windows, MacOS และ Linux มันเป็นตัวจัดการ library ที่ทำงานบน command line ทั้งนี้หลังจากติดตั้งเสร็จ เราจะทำให้มันทำงานร่วมกับ Visual Studio ได้เลย โดยตัว package ที่ติดตั้งมาจะถูกพบโดย Visual Studio ทำให้เราไม่ต้องตั้งค่าวุ่นวายอีก

นอกจากนั้น ตัว vcpkg สามารถทำงานร่วมกับ CMAKE ได้อีกด้วย (แต่เดี๋ยวจะพูดถึงตรงนี้อีกทีทีหลังครับ)

การติดตั้ง VCPKG

การติดตั้งนั้นง่ายมาก วิธีติดตั้งทำตามบน github ได้เลย ซึ่งจริง ๆ บน Windows ก็ทำแค่

> git clone https://github.com/Microsoft/vcpkg.git
> cd vcpkg
> .\bootstrap-vcpkg.bat
> .\vcpkg integrate install

แค่นั้นเอง

ติดตั้ง package บน VCPKG

ตัว package ต่าง ๆ สามารถติดตั้งได้ง่าย ๆ ด้วยคำสั่ง vcpkg install <packagename> เช่น vcpkg install sfml เป็นต้น

VCPKG จะทำการดาวน์โหลดซอร์สโค๊ดของ package ทั้งที่เราเป็นคนใส่ และ dependency ทั้งหมดของ package ที่เราเลือก (ถ้ายังไม่มีแพคเกจดังกล่าวในระบบ) จากนั้นมันจะทำการ build package ทั้งหมดในเครื่องเรา และติดตั้งไปใน directory ของมันเอง

การใช้งานใน Visual Studio

อันนี้ง่ายมาก คือ library ที่เราติดตั้งเนี่ยครับ เราสามารถ #include ได้เลย ไม่ต้องแก้อะไรอีก และเมื่อ #include แล้ว VS จะ link library ที่เราใช้ให้อัตโนมัติ

อย่างอันนี้ผมแค่สร้างโปรเจคใหม่ขึ้นมา เพิ่มไฟล์ใหม่เข้าไป แล้วก็สั่ง build ใช้งานได้เลย ง่ายมาก

การใช้งานกับ CMake

สำหรับโปรเจคที่เป็น CMake เนี่ย เราจะสร้างไฟล์ CMakelist.txt โดยนอกจากที่เราจะเพิ่มไฟล์เข้าไปในโปรเจคแล้ว เราก็จะเพิ่มพวกคำสั่ง find_package() กับ target_link_libraries() เข้าไปอีกหน่อยนึง โดยตอนที่เราติดตั้งแพคเกจ vcpkg จะแนะนำว่าให้ใส่อะไรลงไปบ้าง ….

-- Performing post-build validation done
Building package sfml[core]:x64-windows... done
Installing package sfml[core]:x64-windows...
Installing package sfml[core]:x64-windows... done
Elapsed time for package sfml:x64-windows: 35.96 s

Total elapsed time: 3.321 min

The package sfml:x64-windows provides CMake targets:

    find_package(SFML CONFIG REQUIRED)
    # Note: 7 target(s) were omitted.
    target_link_libraries(main PRIVATE FLAC OpenAL OpenGL Vorbis)

อย่างอันนี้ผมติดตั้ง SFML มันก็จะแนะนำมาเลยว่า ต้องใส่ find_package(SFML CONFIG REQUIRED) กับ target_link_libraries(main PRIVATE FLAC OpenAL OpenGL Vorbis) ใน CMakelists.txt นะ

แต่กรณีของ SFML เนี่ยคนที่เขาทำ package เขาเหมือนจะลืมบอกเรื่องของ module ไปครับ เราก็ต้องไปใส่ให้มันครบ (ฮา) ก็จะวุ่นวายหน่อยนึง อย่างไฟล์ CMakelists.txt ของผมจะมีหน้าตาประมาณนี้ ถึงจะบิลด์โปรเจคผ่าน

cmake_minimum_required(VERSION 3.15)

project(testSFML)

add_executable(main "main.cpp")

find_package(SFML CONFIG COMPONENTS system window graphics REQUIRED)
target_link_libraries(main PRIVATE FLAC OpenAL OpenGL Vorbis sfml-graphics)

ถึงจุดนี้ ถ้าสมมติว่าผมใช้ Visual Studio ผมสามารถที่จะเปิดโปรเจคนี้โดยการเลือก File > Open > Folder... แล้ว Visual Studio ก็จะเปิดโปรเจคขึ้นมาด้วยตัว CMake Support ของ VS เอง

หรือถ้าจะไม่ใช้ Visual Studio CMake Support เราก็สามารถใช้ CMake เพื่อสร้างตัว Build File ด้วยวิธีปรกติครับ แต่จะต้องใช้ CMake Toolchain file ของ VCPKG ด้วย option -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake

ซึ่งถ้าจำไม่ได้ว่าไฟล์อยู่ไหน ให้ลองรัน vcpkg integrate install มันจะแสดงคำสั่งนี้ขึ้นมาให้ครับ

“Triplet”

Triplet เป็นคอนเซพท์ของ VCPKG ที่เหมือน platform ต่าง ๆ ที่ VCPKG รองรับ โดยก็จะมีส่วนของ isa และส่วนของ os เช่น x86-windows หรือ x64-windows ซึ่ง ถ้าโปรเจคที่เราทำงานด้วยนั้นใช้ target ที่ไม่ตรงกับ triplet ที่เราติดตั้ง โปรเจคเราก็จะ build ไม่ผ่านครับ

ทีนี้ ตอนที่เราติดตั้ง package เราสามารถระบุได้เลยว่าจะใช้ triplet ไหน โดยใช้ : คั่นระหว่าง package และ triplet

เช่น vcpkg install sfml:x86-windows

ซึ่งถ้าเราไม่ระบุ มันจะใช้ค่า default โดยค่า default บน Windows ก็จะเป็น x86-windows แต่เราสามารถเพิ่ม environment variable ชื่อว่า VCPKG_DEFAULT_TRIPLET เพื่อเปลี่ยนตัว default เป็น triplet ที่เราต้องการได้เลยครับ