diff --git a/.github/workflows/cmake-ci.yml b/.github/workflows/cmake-ci.yml new file mode 100644 index 000000000..f88130328 --- /dev/null +++ b/.github/workflows/cmake-ci.yml @@ -0,0 +1,17 @@ +name: cmake ci +on: [workflow_dispatch] +jobs: + build: + runs-on: macos-14 + steps: + - name: Checkout last commit + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Configure build environment + run: | + brew install ninja rsync + - name: Build + run: | + cmake -S. -GNinja -DCMAKE_OSX_DEPLOYMENT_TARGET=13 + ninja -Cbuild diff --git a/Assets.xcassets/RimeIcon.appiconset/Contents.json b/Assets.xcassets/RimeIcon.appiconset/Contents.json index acfdfc399..1553a9150 100644 --- a/Assets.xcassets/RimeIcon.appiconset/Contents.json +++ b/Assets.xcassets/RimeIcon.appiconset/Contents.json @@ -1,61 +1,61 @@ { "images" : [ { - "filename" : "rime-16 1.png", + "filename" : "icon-16x16.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { - "filename" : "rime-32 1.png", + "filename" : "icon-16x16@2.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { - "filename" : "rime-32.png", + "filename" : "icon-32x32.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { - "filename" : "rime-64.png", + "filename" : "icon-32x32@2.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { - "filename" : "rime-128.png", + "filename" : "icon-128x128.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { - "filename" : "rime-256.png", + "filename" : "icon-128x128@2.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { - "filename" : "rime-256.png", + "filename" : "icon-256x256.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { - "filename" : "rime-512.png", + "filename" : "icon-256x256@2.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { - "filename" : "rime-512.png", + "filename" : "icon-512x512.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { - "filename" : "rime-1024.png", + "filename" : "icon-512x512@2.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-128.png b/Assets.xcassets/RimeIcon.appiconset/icon-128x128.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-128.png rename to Assets.xcassets/RimeIcon.appiconset/icon-128x128.png diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-256.png b/Assets.xcassets/RimeIcon.appiconset/icon-128x128@2.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-256.png rename to Assets.xcassets/RimeIcon.appiconset/icon-128x128@2.png diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-16 1.png b/Assets.xcassets/RimeIcon.appiconset/icon-16x16.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-16 1.png rename to Assets.xcassets/RimeIcon.appiconset/icon-16x16.png diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-32 1.png b/Assets.xcassets/RimeIcon.appiconset/icon-16x16@2.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-32 1.png rename to Assets.xcassets/RimeIcon.appiconset/icon-16x16@2.png diff --git a/Assets.xcassets/RimeIcon.appiconset/icon-256x256.png b/Assets.xcassets/RimeIcon.appiconset/icon-256x256.png new file mode 100644 index 000000000..e12ead07c Binary files /dev/null and b/Assets.xcassets/RimeIcon.appiconset/icon-256x256.png differ diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-512.png b/Assets.xcassets/RimeIcon.appiconset/icon-256x256@2.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-512.png rename to Assets.xcassets/RimeIcon.appiconset/icon-256x256@2.png diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-32.png b/Assets.xcassets/RimeIcon.appiconset/icon-32x32.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-32.png rename to Assets.xcassets/RimeIcon.appiconset/icon-32x32.png diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-64.png b/Assets.xcassets/RimeIcon.appiconset/icon-32x32@2.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-64.png rename to Assets.xcassets/RimeIcon.appiconset/icon-32x32@2.png diff --git a/Assets.xcassets/RimeIcon.appiconset/icon-512x512.png b/Assets.xcassets/RimeIcon.appiconset/icon-512x512.png new file mode 100644 index 000000000..a0c224363 Binary files /dev/null and b/Assets.xcassets/RimeIcon.appiconset/icon-512x512.png differ diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-1024.png b/Assets.xcassets/RimeIcon.appiconset/icon-512x512@2.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-1024.png rename to Assets.xcassets/RimeIcon.appiconset/icon-512x512@2.png diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..a1e95ff14 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.30) +project(squirrel VERSION 1.0.2 LANGUAGES CXX Swift) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +# ############################################################################## +# Options +# ############################################################################## +set(DEV_ID "UNSET" CACHE STRING "Dev ID for code-signing") +set(SIGN_KEY "UNSET" CACHE STRING "Sign key for code-signing") +option(FIXUP_WHEN_BUILD "Fix-up the app bundle in build time" ON) + +if(NOT (DEV_ID STREQUAL "UNSET") AND (SIGN_KEY STREQUAL "UNSET")) + set(SIGN_KEY "Developer ID Application: ${DEV_ID}") +endif() + +# ############################################################################## +# Dependencies +# ############################################################################## +include(AddSparkle) +include(AddRime) + +# ############################################################################## +# Squirrel.app +# ############################################################################## +file(GLOB SRCS "${PROJECT_SOURCE_DIR}/Sources/*.swift") +add_executable(Squirrel MACOSX_BUNDLE ${SRCS}) +target_compile_options(Squirrel PRIVATE + -import-objc-header "${PROJECT_SOURCE_DIR}/Sources/Squirrel-Bridging-Header.h" + -enable-bare-slash-regex +) +target_include_directories(Squirrel PRIVATE + "${PROJECT_SOURCE_DIR}/Sources" + "${PROJECT_SOURCE_DIR}/librime/src" +) +target_link_libraries(Squirrel PRIVATE rime Sparkle) + +set(BUNDLE_PATH "${PROJECT_BINARY_DIR}/Squirrel.app") +set(BUNDLE_CONTENTS_DIR "${BUNDLE_PATH}/Contents") +set(BUNDLE_BIN_DIR "${BUNDLE_CONTENTS_DIR}/MacOS") +set(BUNDLE_RESOURCES_DIR "${BUNDLE_CONTENTS_DIR}/Resources") +set(BUNDLE_FRAMEWORKS_DIR "${BUNDLE_CONTENTS_DIR}/Frameworks") +set(BUNDLE_SHARED_SUPPORT_DIR "${BUNDLE_CONTENTS_DIR}/SharedSupport") + +set_target_properties(Squirrel PROPERTIES + MACOSX_BUNDLE_GUI_IDENTIFIER im.rime.inputmethod.Squirrel + MACOSX_BUNDLE_BUNDLE_VERSION ${CMAKE_PROJECT_VERSION} +) + +# Copy additional binaries +file(GLOB RIME_PROGRAMS "${CMAKE_BINARY_DIR}/librime/bin/rime_*") +add_custom_command(TARGET Squirrel POST_BUILD + COMMAND cp -a "${CMAKE_BINARY_DIR}/librime/bin/rime_*" ${BUNDLE_BIN_DIR}) + +# Copy resources +add_subdirectory(resources) + +# Copy built-in schemas +add_subdirectory(data) + +# Copy linked frameworks and libraries +add_custom_command(TARGET Squirrel POST_BUILD + COMMAND rsync -a "${CMAKE_CURRENT_BINARY_DIR}/librime/lib/" "${BUNDLE_FRAMEWORKS_DIR}" + --exclude pkgconfig +) +add_custom_command(TARGET Squirrel POST_BUILD + COMMAND rsync -a "${CMAKE_BINARY_DIR}/Sparkle.framework" + "${BUNDLE_FRAMEWORKS_DIR}" + --exclude=Headers --exclude=PrivateHeaders --exclude=Modules +) + +# Fix linking so that the bundle is standalone +set(FIXUP_SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/fixup.cmake) +set(FIXUP_DIRS "${BUNDLE_FRAMEWORKS_DIR}") +file(WRITE ${FIXUP_SCRIPT} " + include(BundleUtilities) + file(GLOB PLUGINS ${BUNDLE_FRAMEWORKS_DIR}/rime-plugins/*.dylib) + fixup_bundle(\"${BUNDLE_PATH}\" \"\${PLUGINS}\" \"${FIXUP_DIRS}\") +") +if(${FIXUP_WHEN_BUILD}) + add_custom_command(TARGET Squirrel POST_BUILD + COMMAND ${CMAKE_COMMAND} -P ${FIXUP_SCRIPT} + ) +endif() + +# Install (for development) +set(CMAKE_SKIP_RPATH TRUE) # done by fixup_bundle +install(SCRIPT ${FIXUP_SCRIPT}) +install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target sign)") +install(TARGETS Squirrel BUNDLE DESTINATION "/Library/Input\ Methods") + +# ############################################################################## +# Distribution +# ############################################################################## +add_subdirectory(package) diff --git a/INSTALL.md b/INSTALL.md index 3483eb4c9..16681b2cd 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -182,3 +182,20 @@ make clean-package If you want to clean all above, do all. That's it, a verbal journal. Thanks for riming with Squirrel. + +## Build with CMake + +Alternatively, Squirrel can be built with CMake. Universal binaries are not supported. + +Note that universal binaries are not supported, and Xcode command line tools are still required. + +```sh +cmake -S. -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 -DDEV_ID='YourDevID' +cd build +ninja # Create build/Squirrel.app +ninja sign # Code-sign build/Squirrel.app (*) +ninja package # Create package/Squirrel.pkg (*) +ninja install # Directly install to '/Library/Input Methods' (*) +``` + +(*) `sign`, `install`, and `package` require code-signing `DEV_ID` or `SIGN_KEY` to be set. diff --git a/action-install.sh b/action-install.sh index d5a0cbdb1..776417a87 100755 --- a/action-install.sh +++ b/action-install.sh @@ -3,7 +3,7 @@ set -e rime_version=latest -rime_git_hash=24f0f7b +rime_git_hash=0cf0e63 sparkle_version=2.6.2 rime_archive="rime-${rime_git_hash}-macOS-universal.tar.bz2" diff --git a/cmake/AddRime.cmake b/cmake/AddRime.cmake new file mode 100644 index 000000000..32ca3baa8 --- /dev/null +++ b/cmake/AddRime.cmake @@ -0,0 +1,22 @@ +include(ExternalProject) + +ExternalProject_Add(BuildRime + SOURCE_DIR "${PROJECT_SOURCE_DIR}/librime" + INSTALL_DIR "${PROJECT_BINARY_DIR}/librime" + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} + -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} + -DBUILD_MERGED_PLUGINS=OFF + -DENABLE_EXTERNAL_PLUGINS=ON + -DBUILD_TEST=OFF +) + +add_library(rime INTERFACE) +add_dependencies(rime BuildRime) +target_include_directories(rime INTERFACE "${PROJECT_BINARY_DIR}/librime/include") +target_link_directories(rime INTERFACE "${PROJECT_BINARY_DIR}/librime/lib/") +target_link_libraries(rime INTERFACE -lrime) + +find_path(X11Keysym X11/keysym.h) +target_include_directories(rime INTERFACE ${X11Keysym}) diff --git a/cmake/AddSparkle.cmake b/cmake/AddSparkle.cmake new file mode 100644 index 000000000..d0bd9b4cc --- /dev/null +++ b/cmake/AddSparkle.cmake @@ -0,0 +1,18 @@ +include(ExternalProject) + +ExternalProject_Add(BuildSparkle + SOURCE_DIR "${PROJECT_SOURCE_DIR}/Sparkle" + + CONFIGURE_COMMAND "" + + BUILD_COMMAND xcodebuild -project "/Sparkle.xcodeproj" -scheme Sparkle -configuration Release + BUILD_IN_SOURCE TRUE + + # Install Sparkle.framework + INSTALL_COMMAND cp -a "/build/Release/Sparkle.framework" "${CMAKE_BINARY_DIR}" +) + +add_library(Sparkle INTERFACE) +add_dependencies(Sparkle BuildSparkle) +target_include_directories(Sparkle INTERFACE "${CMAKE_BINARY_DIR}/Sparkle.framework/Headers") +target_link_libraries(Sparkle INTERFACE "${CMAKE_BINARY_DIR}/Sparkle.framework") diff --git a/cmake/AppleUtils.cmake b/cmake/AppleUtils.cmake new file mode 100644 index 000000000..4fbe4e816 --- /dev/null +++ b/cmake/AppleUtils.cmake @@ -0,0 +1,10 @@ +macro(process_xcstrings path outdir) + get_filename_component(basename ${path} NAME_WLE) + set(target ${outdir}/en.lproj/${basename}.strings) + add_custom_command( + OUTPUT ${target} + COMMAND xcrun xcstringstool compile ${path} --output-directory ${outdir} + COMMENT "Process ${basename}.xcstrings" + ) + add_custom_target(xcstrings_${basename} ALL DEPENDS ${target}) +endmacro() diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt new file mode 100644 index 000000000..7cf9a1c4f --- /dev/null +++ b/data/CMakeLists.txt @@ -0,0 +1,21 @@ +set(OUTDIR "${BUNDLE_SHARED_SUPPORT_DIR}") + +# ############################################################################## +# Plum +# ############################################################################## +add_custom_command( + OUTPUT "${OUTDIR}/default.yaml" + COMMAND make "OUTPUT=${OUTDIR}" -C "${PROJECT_SOURCE_DIR}/plum" + COMMENT "plum: update default schemas") +add_custom_target(plum-data ALL + DEPENDS "${OUTDIR}/default.yaml") + +# ############################################################################## +# OpenCC +# ############################################################################## +add_custom_command( + OUTPUT "${OUTDIR}/opencc/s2t.json" + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/librime/share/opencc ${OUTDIR}/opencc + COMMENT "opencc: copy data") +add_custom_target(opencc-data ALL + DEPENDS "${OUTDIR}/opencc/s2t.json") diff --git a/librime b/librime index 6b1b41f53..0cf0e63b3 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 6b1b41f53cd7fb8cd605c65cfb1e8d5c780f1308 +Subproject commit 0cf0e63b3a5460541bdd5110ec8f4bd31d647885 diff --git a/package/CMakeLists.txt b/package/CMakeLists.txt new file mode 100644 index 000000000..4b0caf82b --- /dev/null +++ b/package/CMakeLists.txt @@ -0,0 +1,12 @@ +add_custom_target(sign + COMMAND codesign --deep --force --options runtime --timestamp --verbose + --entitlements "${PROJECT_SOURCE_DIR}/resources/Squirrel.entitlements" + --sign "${SIGN_KEY}" "${BUNDLE_PATH}" + DEPENDS Squirrel +) + +add_custom_target(package + COMMAND ./make_package + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + DEPENDS sign +) diff --git a/package/make_package b/package/make_package index 61ce71b69..6efc96ac9 100755 --- a/package/make_package +++ b/package/make_package @@ -7,9 +7,16 @@ INSTALL_LOCATION='/Library/Input Methods' cd "$(dirname $0)" source common.sh +if [ -e "${PROJECT_ROOT}/build/Squirrel.app" ]; then + ROOT=`mktemp -d` + cp -a "${PROJECT_ROOT}/build/Squirrel.app" "$ROOT" +else + ROOT="${PROJECT_ROOT}/${DERIVED_DATA_PATH}/Build/Products/Release" +fi + pkgbuild \ --info PackageInfo \ - --root "${PROJECT_ROOT}/${DERIVED_DATA_PATH}/Build/Products/Release" \ + --root "$ROOT" \ --filter '.*\.swiftmodule$' \ --component-plist Squirrel-component.plist \ --identifier "${BUNDLE_IDENTIFIER}" \ diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt new file mode 100644 index 000000000..9ba96cc96 --- /dev/null +++ b/resources/CMakeLists.txt @@ -0,0 +1,31 @@ +# Info.plist +file(READ "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" INFO_PLIST) +string(REPLACE "$(PRODUCT_BUNDLE_IDENTIFIER)" "@MACOSX_BUNDLE_GUI_IDENTIFIER@" INFO_PLIST "${INFO_PLIST}") +string(REPLACE "$(CURRENT_PROJECT_VERSION)" "@MACOSX_BUNDLE_BUNDLE_VERSION@" INFO_PLIST "${INFO_PLIST}") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/Info.plist" "${INFO_PLIST}") +set_target_properties(Squirrel PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist +) + +# Static resources +set(FILES + ${PROJECT_SOURCE_DIR}/README.md + ${PROJECT_SOURCE_DIR}/LICENSE.txt + rime.pdf +) +foreach(f ${FILES}) + get_filename_component(filename ${f} NAME) + configure_file(${f} ${BUNDLE_RESOURCES_DIR}/${filename} COPYONLY) +endforeach() + +# Localization +include(AppleUtils) +process_xcstrings(${CMAKE_CURRENT_SOURCE_DIR}/Localizable.xcstrings ${BUNDLE_RESOURCES_DIR}) +process_xcstrings(${CMAKE_CURRENT_SOURCE_DIR}/InfoPlist.xcstrings ${BUNDLE_RESOURCES_DIR}) + +# RimeIcon.icns +add_custom_command( + OUTPUT ${BUNDLE_RESOURCES_DIR}/RimeIcon.icns + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/Assets.xcassets/RimeIcon.appiconset ${CMAKE_BINARY_DIR}/RimeIcon.iconset && iconutil -c icns ${CMAKE_BINARY_DIR}/RimeIcon.iconset -o ${BUNDLE_RESOURCES_DIR}/RimeIcon.icns +) +add_custom_target(RimeIcon.icns ALL DEPENDS ${BUNDLE_RESOURCES_DIR}/RimeIcon.icns)