skip to Main Content

I’m currently working on a Flutter project and I tend to use VSCode, this is my configuration:

Json:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "UAT",
            "request": "launch",
            "type": "dart",
            "program": "lib/main.dart",
            "preLaunchTask": "UAT",
            "args": [
                "--target",
                "lib/main.dart",
                "--dart-define-from-file=env.json",
            ],
        }
    ]
}

Which basically runs a task that has this command:

Json:

"command": "setup.sh uat",

Thing is that I’m trying to find a way for my colleges using AndroidStudio to get a similar experience by using the runConfigurations in AndroidStudio

I have tried adding external tools, and it worked just fine… But you cannot add them to git because it’s stored locally, so it refers to a tool u don’t have

I also thought about adding it to build.gradle, but I do not manage to get it working

Is there any advice u guys could give me?

Thanks! 🙂

2

Answers


  1. Chosen as BEST ANSWER

    After some tries I managed to get a build.gradle task which is in fact checked into the vcs :')

    What i did was:

    Created .idea/runConfigurations/whatever.xml

    <component name="ProjectRunConfigurationManager">
      <configuration default="false" name="QA" type="FlutterRunConfigurationType" factoryName="Flutter">
        <option name="additionalArgs" value="--dart-define=X=X --dart-define-from-file=env.json" />
        <option name="filePath" value="$PROJECT_DIR$/lib/main.dart" />
        <method v="2">
          <option name="Gradle.BeforeRunTask" enabled="false" tasks="runCopyScript" externalProjectPath="$PROJECT_DIR$/android/scripts" vmOptions="" scriptParameters="-Penv=qa" />
        </method>
      </configuration>
    </component>
    

    So if I wanted to define a variable I'd just use a regular dart define and also a dart-define-from-file just as an example

    The important part is inside the ...

    I just created another build.gradle under android/scripts/ (Wanted to have it separate from the main build.gradle)

    In that script you can do something like:

    task runCopyScript(type: Exec) {
        workingDir "../../"
    
        def scriptFile = './whatever/my_script.sh'
        def environment = project.property('env')
    
        println "Executing: ${scriptFile} ${environment}"
    
        commandLine 'bash', scriptFile, "${environment}"
    }
    

    And this will run the script with the argument u passed as -Penv (in this case, "qa")

    one last thing u would need to do is to modify the /android/settings.gradle` to actually add this sub-folder

    just add at the very bottom:

    ...// previous code i just place it after the [include ":app"] line
    include ":scripts"
    

  2. I’ve created a script that takes the ".vscode/launch.json" and based on it (and the preLaunchTask it might have) it generates both ".idea/runConfigurations/[config].xml’s" and some scripts, that get’s added to my Makefile (if you use command line tool or want to run it from pipelines)

    #!/bin/bash
    
    # Generate run configurations from launch.json
    # Usage: ./generate.sh
    
    # Colors
    RESET='33[0m'
    SUCCESS='33[0;32m'
    WARN='33[0;33m'
    ERROR='33[0;31m'
    
    # Install jq if not installed
    if ! command -v jq &>/dev/null; then
        echo -e "${WARN}jq not found, installing...${RESET}"
    
        # Try to install using brew
        if command -v brew &>/dev/null; then
            brew install jq
        else
            echo -e "${ERROR}brew not found, please install jq manually${RESET}"
            exit 1
        fi
    fi
    
    # Launch.json file
    LAUNCH_JSON_FILE=".vscode/launch.json"
    # Run configurations directory
    RUN_CONFIGURATIONS_DIR=".idea/runConfigurations"
    # Scripts directory
    SCRIPTS_DIR="scripts/configs"
    # Tasks.json file
    TASKS_JSON_FILE=".vscode/tasks.json"
    # Android Studio build.gradle file
    ANDROID_GRADLE_FILE="android/scripts/build.gradle"
    # Android settings.gradle file
    ANDROID_SETTINGS_GRADLE="android/settings.gradle"
    # Makefile path
    MAKEFILE="Makefile"
    
    # Check if launch.json file exists
    if [ ! -f "$LAUNCH_JSON_FILE" ]; then
        echo -e "${ERROR}$LAUNCH_JSON_FILE not found: ${RESET}"
        exit 1
    fi
    
    # Check if tasks.json file exists
    if [ ! -f "$TASKS_JSON_FILE" ]; then
        echo -e "${WARN}$TASKS_JSON_FILE not found, preLaunchTasks will be ignored${RESET}"
    fi
    
    # Check if run configurations directory exists, if it does, cleanup, if not, create it
    if [ ! -d "$RUN_CONFIGURATIONS_DIR" ]; then
        echo -e "${WARN}$RUN_CONFIGURATIONS_DIR not found${RESET}"
        echo -e "Creating run configurations directory..."
        mkdir -p $RUN_CONFIGURATIONS_DIR
    else
        echo -e "Cleaning up run configurations directory..."
        rm -rf $RUN_CONFIGURATIONS_DIR/*
    fi
    
    # Check if scripts directory exists, if not, create it
    if [ ! -d "$SCRIPTS_DIR" ]; then
        echo -e "${WARN}$SCRIPTS_DIR not found${RESET}"
        echo -e "Creating scripts directory..."
        mkdir -p $SCRIPTS_DIR
    else
        echo -e "Cleaning up scripts directory..."
        rm -rf $SCRIPTS_DIR/*
    fi
    
    # Ensure the directory for build.gradle exists
    mkdir -p $(dirname $ANDROID_GRADLE_FILE)
    
    # Initialize build.gradle content
    echo "" >$ANDROID_GRADLE_FILE
    
    # Function to get task command
    get_task_command() {
        local task_label=$1
        jq -r ".tasks[] | select(.label == "$task_label") | .command" $TASKS_JSON_FILE
    }
    
    # Function to clean empty lines at the end of a file
    clean_empty_lines() {
        local file=$1
        # Remove empty lines at the end of the file
        sed -i '' -e :a -e '/^n*$/{$d;N;ba' -e '}' "$file"
        # Add a newline at the end of the file
        echo >>"$file"
    }
    
    # Function to update Makefile
    update_makefile() {
        local environments=""
        local makefile_content=""
    
        # Extract unique environment names from configurations
        while read -r CONFIGURATION; do
            ENV_NAME=$(echo $CONFIGURATION | jq -r '.name' | cut -d ' ' -f1)
            if [[ ! $environments =~ $ENV_NAME ]]; then
                environments+="$ENV_NAME "
            fi
        done <<< "$CONFIGURATIONS"
    
        # Generate Makefile content
        makefile_content=$(cat << EOF
    # Environment targets | ⚠️ DO NOT ADD ANYTHING BELOW THIS LINE
    .PHONY: $(echo $environments | tr '[:upper:]' '[:lower:]')
    $(for env in $environments; do
        env_lower=$(echo $env | tr '[:upper:]' '[:lower:]')
        echo "$env_lower: ## Build/Run for $env environment. Use 'make $env_lower [args="build|run [additional args]"]'"
    done)
    $(echo $environments | tr '[:upper:]' '[:lower:]'):
        @if [ -z "$(args)" ]; then \
            ./scripts/configs/$(shell printf '%s' "$@" | tr '[:lower:]' '[:upper:]').sh run; \
        else \
            ./scripts/configs/$(shell printf '%s' "$@" | tr '[:lower:]' '[:upper:]').sh $(args); \
        fi
    EOF
    )
    
        # Update Makefile
        if [ -f "$MAKEFILE" ]; then
            # Remove existing environment targets section
            sed -i '' '/^# Environment targets | ⚠️ DO NOT ADD ANYTHING BELOW THIS LINE/,/^$/d' "$MAKEFILE"
            
            # Append new environment targets
            echo "$makefile_content" >> "$MAKEFILE"
            
            echo -e "${SUCCESS}Updated Makefile with new environment targets${RESET}"
        else
            echo -e "${WARN}Makefile not found. Creating a new one with environment targets${RESET}"
            echo "$makefile_content" > "$MAKEFILE"
        fi
    }
    
    # Get configurations from launch.json
    CONFIGURATIONS=$(jq -c '.configurations[]' $LAUNCH_JSON_FILE)
    
    echo "Generating run configurations..."
    
    HAS_PRE_LAUNCH_TASKS=false
    
    # Iterate over configurations
    while read -r CONFIGURATION; do
        # Get configuration name
        CONFIGURATION_NAME=$(echo $CONFIGURATION | jq -r '.name')
        # Get configuration program
        CONFIGURATION_PROGRAM=$(echo $CONFIGURATION | jq -r '.program')
        # Get configuration args
        CONFIGURATION_ARGS=$(echo $CONFIGURATION | jq -r '.args | join(" ")')
        # Get preLaunchTask if it exists
        PRE_LAUNCH_TASK=$(echo $CONFIGURATION | jq -r '.preLaunchTask // empty')
    
        # Create run configuration file
        cat <<EOF >$RUN_CONFIGURATIONS_DIR/$CONFIGURATION_NAME.xml
    <component name="ProjectRunConfigurationManager">
      <configuration default="false" name="$CONFIGURATION_NAME" type="FlutterRunConfigurationType" factoryName="Flutter">
        <option name="additionalArgs" value="$CONFIGURATION_ARGS" />
        <option name="filePath" value="$PROJECT_DIR$/$CONFIGURATION_PROGRAM" />
        <method v="2">
    EOF
    
        # Add Gradle task if preLaunchTask exists
        if [ -n "$PRE_LAUNCH_TASK" ]; then
            HAS_PRE_LAUNCH_TASKS=true
            TASK_COMMAND=$(get_task_command "$PRE_LAUNCH_TASK")
            if [ -n "$TASK_COMMAND" ]; then
                echo "      <option name="Gradle.BeforeRunTask" enabled="true" tasks="$CONFIGURATION_NAME" externalProjectPath="$PROJECT_DIR$/android/scripts" vmOptions="" scriptParameters="" />" >>$RUN_CONFIGURATIONS_DIR/$CONFIGURATION_NAME.xml
    
                # Add task to build.gradle
                cat <<EOF >>$ANDROID_GRADLE_FILE
    task $CONFIGURATION_NAME(type: Exec) {
        workingDir "../../"
        def scriptFile = '$TASK_COMMAND'
        println "Executing: ${scriptFile}"
        commandLine 'bash', scriptFile
    }
    
    EOF
            fi
        fi
    
        echo "    </method>" >>$RUN_CONFIGURATIONS_DIR/$CONFIGURATION_NAME.xml
        echo "  </configuration>" >>$RUN_CONFIGURATIONS_DIR/$CONFIGURATION_NAME.xml
        echo "</component>" >>$RUN_CONFIGURATIONS_DIR/$CONFIGURATION_NAME.xml
    
        # Create script file for running the configuration
        SCRIPT_FILE="$SCRIPTS_DIR/$CONFIGURATION_NAME.sh"
        cat <<EOF >$SCRIPT_FILE
    #!/bin/bash
    
    # Usage: ./$CONFIGURATION_NAME.sh [run|build] [additional build args...]
    # If no action is provided, it defaults to 'run'
    # For 'build', additional arguments can be provided (e.g., ios --no-codesign, aab, etc.)
    
    ACTION=${1:-run}
    
    # Add preLaunchTask command if it exists
    if [ -n "$PRE_LAUNCH_TASK" ]; then
        TASK_COMMAND=$(get_task_command "$PRE_LAUNCH_TASK")
        if [ -n "$TASK_COMMAND" ]; then
            $TASK_COMMAND
        fi
    fi
    
    if [ "$ACTION" = "run" ]; then
        flutter run $CONFIGURATION_ARGS --target=$CONFIGURATION_PROGRAM
    elif [ "$ACTION" = "build" ]; then
        shift 1  # Remove action argument
        flutter build $@ $CONFIGURATION_ARGS --target=$CONFIGURATION_PROGRAM
    else
        echo "Invalid action. Use 'run' or 'build'."
        exit 1
    fi
    EOF
        chmod +x $SCRIPT_FILE
    
        echo -e "${SUCCESS}Generated $CONFIGURATION_NAME${RESET}"
    done <<<"$CONFIGURATIONS"
    
    # Check if there are any preLaunchTasks
    if [ "$HAS_PRE_LAUNCH_TASKS" = false ]; then
        echo "No preLaunchTasks found. Removing $ANDROID_GRADLE_FILE..."
        rm -f $ANDROID_GRADLE_FILE
    
        # Remove include ":scripts" from settings.gradle if it exists
        if [ -f "$ANDROID_SETTINGS_GRADLE" ]; then
            sed -i '' '/include ":scripts"/d' "$ANDROID_SETTINGS_GRADLE"
            echo "Removed 'include ":scripts"' from settings.gradle"
            clean_empty_lines "$ANDROID_SETTINGS_GRADLE"
        fi
    else
        echo "PreLaunchTasks found. Keeping $ANDROID_GRADLE_FILE..."
        # Update settings.gradle
        if [ -f "$ANDROID_SETTINGS_GRADLE" ]; then
            if ! grep -q "include ":scripts"" "$ANDROID_SETTINGS_GRADLE"; then
                echo "Updating $ANDROID_SETTINGS_GRADLE..."
                echo "include ":scripts"" >>"$ANDROID_SETTINGS_GRADLE"
            fi
            clean_empty_lines "$ANDROID_SETTINGS_GRADLE"
        else
            echo -e "${WARN}$ANDROID_SETTINGS_GRADLE not found${RESET}"
        fi
    fi
    
    update_makefile
    
    # Check if git is installed, if not, print a warning otherwise add run configurations and scripts to git
    if ! command -v git &>/dev/null; then
        echo -e "${WARN}git not found, please install git, or manually add $RUN_CONFIGURATIONS_DIR and $SCRIPTS_DIR to git${RESET}"
    else
        echo "Adding run configurations and scripts to git..."
        git add -f $RUN_CONFIGURATIONS_DIR $SCRIPTS_DIR $MAKEFILE
        if [ -f "$ANDROID_GRADLE_FILE" ]; then
            git add -f $ANDROID_GRADLE_FILE
        fi
        if [ -f "$ANDROID_SETTINGS_GRADLE" ]; then
            git add -f $ANDROID_SETTINGS_GRADLE
        fi
    fi
    
    # Done
    echo -e "${SUCCESS}Done!${RESET}"
    
    exit 0
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search