XcodeGen icon indicating copy to clipboard operation
XcodeGen copied to clipboard

Enhancement: copyFiles buildPhase using Products

Open aerobounce opened this issue 4 years ago • 7 comments

First thing first, let me show you concept:

Note that this is just a concept and is not a valid syntax

targets:
    Sample Application:
        type: application
        platform: macOS
        sources:
              target: Helper
              optional: true
              buildPhase:
                  copyFiles:
                      destination: wrapper
                      subpath: $(CONTENTS_FOLDER_PATH)/Library/LoginItems
    Helper:
        type: application
        platform: macOS
  • Here I'm creating Sample Application which is the main application target, and the helper application Helper. Helper application will be used to toggle Launch on Login feature which is seen in many applications. To make it work, a helper application must be included in the main app bundle, in this case it's: Sample Application.app/Contents/Library/LoginItems/Helper.app.
    • This is the latest and valid way to achieve the feature as of today.
    • Reference: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLoginItems.html

Back to xcodegen, if I'm correct, this is not possible as of v2.18.0 (Please correct me if I'm missing something). The above is just an example to explain the usefulness if it's possible. And this is not specific to the LoginItem thing, there may be other cases that can use this approach, I guess. Thus, "copyFiles buildPhase using Products". (I'm not sure if sources is the suitable location to put them, this is just a concept)

What do you think?

aerobounce avatar Nov 10 '20 20:11 aerobounce

I'd love to do something similar in my app where I want to have a helper CLI embedded in my app package, I'm just not sure how to do it yet. If this is possible with XcodeGen as it is today I'd love to learn it. To my mind having this be in the sources object feels really weird because it's not part of the sources of my target. I was looking more for a specific copyFiles object separate from the sources object.

jsorge avatar Dec 01 '20 03:12 jsorge

I would like to reference files from the product directory in my build phases as well. 👍

My current workaround for login items contains a post-build shell script, but there is probably a better solution.

LOGIN_ITEMS_DIR="$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app/Contents/Library/LoginItems"
rm -rf "$LOGIN_ITEMS_DIR"
mkdir -p "$LOGIN_ITEMS_DIR"
cp -a "$BUILT_PRODUCTS_DIR/Helper.app" "$LOGIN_ITEMS_DIR"

FelixLisczyk avatar Mar 25 '21 19:03 FelixLisczyk

I'm using post-script to workaround this too.

However I've been thinking that I've had mischosen the repo to open this issue as XcodeGen relies on tuist/XcodeProj to parse project files. Did some research before and if I remember correctly, XcodeProj doesn't support treating a product as a file to use it to like, very this issue's situation.

aerobounce avatar Mar 26 '21 05:03 aerobounce

I've gotten this working by doing 2 things: 1) add embed: false to the app's dependency on the CLI, and 2) this build phase:

    postBuildScripts:
      - script: "ditto \"${SCRIPT_INPUT_FILE_0}\" \"${SCRIPT_OUTPUT_FILE_0}\""
        name: Copy CLI Helper
        inputFiles:
          - ${BUILT_PRODUCTS_DIR}/bindlewriter
        outputFiles:
          - ${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}/Contents/Helpers/bindlewriter

Where in this case bindlewriter is the name of the CLI product that's being embedded inside of my app's bundle at Contents/Helpers. It's helpful for me when I run into situations like this to write the script in my generated project and then copy it in to the right spot in my manifest.

jsorge avatar Mar 26 '21 15:03 jsorge

That's elegant! I like it. What I've reached in the end is far more verbose:

    dependencies: # This assures you the app will be copied into Resources dir
      - target: TARGET NAME OF THE HELPER APP

    postBuildScripts:
      - script: |
          set -Ceux
          HELPER_PRODUCT_NAME="TARGET NAME OF THE HELPER APP"
          CONTENTS_PATH="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}"
          RESOURCES_DIR="${CONTENTS_PATH}/Resources"
          LOGIN_ITEMS_DIR="${CONTENTS_PATH}/Library/LoginItems"
          ORIGIN_HELPER_PATH="${RESOURCES_DIR}/${HELPER_PRODUCT_NAME}.app"

          [[ -d $LOGIN_ITEMS_DIR ]] && rm -rfv "$LOGIN_ITEMS_DIR"
          mkdir -p "$LOGIN_ITEMS_DIR"
          mv -v "$ORIGIN_HELPER_PATH" "$LOGIN_ITEMS_DIR"

          [[ $(ls -A "$RESOURCES_DIR") ]] || rm -rfv "${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Resources"
        name: Move Helper.app to LoginItems

Then, create two main targets: One with the dependency and the script above, and the other without any dependency (for debugging). The benefit of this is you don't have to manually build helper app because it is a dependency.

aerobounce avatar Mar 26 '21 16:03 aerobounce

Yeah I really just mimicked how XcodeGen does other copy files stages and adopted that for my own need! :joy:

jsorge avatar Mar 26 '21 16:03 jsorge

Hope this helps the future:

dependencies:
  - target: LanchAtLoginHelper
    embed: true
    codeSign: false
    copy:
      destination: wrapper
      subpath: Contents/Library/LoginItems

imWildCat avatar Jun 28 '23 05:06 imWildCat