0

First of all, thank you all for trying to help out! Unfortunately, so far no solution has been found yet. I am updating the description to make the problem more reproducible.

  • Update1: use shellcheck.net to clear all other potential issues.
  • Update2: describe the real use case where we run cmake below.

Section 1: minimal case

First found the issue with cmake but it may not be limited to it, by changing cmake in the following minimal reproducible example to make or something else could also reproduce the problem.

#!/bin/bash

set -x

run() {
    arg=$1
    printf '%q\n' "$arg"
    cmake "$arg" 1>/dev/null 2>&1
}

TEXT="local -DCMAKE_ARG=\"arg1;arg2\""

CMAKE_OPTS1=$(echo "$TEXT" | awk '/local/ {for (i=1; i<=NF; i++) {if ($i ~ /^-D/) {printf "%s ", $i}}}' | tr -d '";')
CMAKE_OPTS2=$(echo "$TEXT" | awk '/local/ {for (i=1; i<=NF; i++) {if ($i ~ /^-D/) {printf "%s ", $i}}}' )
CMAKE_OPTS3=("$(echo "$TEXT" | awk '/local/ {for (i=1; i<=NF; i++) {if ($i ~ /^-D/) {printf "%s ", $i}}}' )")

run $CMAKE_OPTS1
run $CMAKE_OPTS2
run "${CMAKE_OPTS3[@]}"

the print out results are (reducing to the lines of interests)

-DCMAKE_ARG=arg1arg2
+ cmake -DCMAKE_ARG=arg1arg2

-DCMAKE_ARG=\"arg1\;arg2\"
+ cmake '-DCMAKE_ARG="arg1;arg2"'

-DCMAKE_ARG=\"arg1\;arg2\"\
+ cmake '-DCMAKE_ARG="arg1;arg2" '

Unfortunately, cmake '-DCMAKE_ARG="arg1;arg2"' does not work properly.

  • CMAKE_OPTS1 is only used for reference, in this case no special characters present in the variable string, and it seems to be working as expected.
  • CMAKE_OPTS2 is the real case where I wish to pass variable containing special characters as argument to a command. Hence the single marks seem to be interfering with command argument parsing.
  • CMAKE_OPTS3 is what I have found from similar issues on this forum, and it does not work either.

Section 2: original script

with lots of modification to minimize the script of course. Hence it involves pulling from a docker and a GitHub repo, both are public and open source.

First, pull docker from Docker Hub (to ensure necessary clang env):

docker pull rocm/miopen:ci

Then within the docker, use the following script:

#!/bin/bash

set -x

build_miopen_ck() {
    echo "Building Composable Kernel"
    ck_commit=$1
    ck_cmake_opts=$2
    if [ -z "$ck_commit" ]; then
        echo "Composable Kernel entry was not found in requirements.txt"
        return
    fi
    mkdir -p /tmp/composable_kernel && cd /tmp/composable_kernel || exit
    wget -nv "https://www.github.com/ROCmSoftwarePlatform/composable_kernel/archive/${ck_commit}.tar.gz"
    tar -xzf "${ck_commit}.tar.gz"
    cd "composable_kernel-${ck_commit}" || exit
    rm -rf build
    mkdir -p build && cd build || exit
    cmake -DBUILD_DEV=OFF \
          -DCMAKE_BUILD_TYPE=Release \
          -DCMAKE_CXX_COMPILER="/opt/rocm/llvm/bin/clang++" \
          -DCMAKE_C_COMPILER="/opt/rocm/llvm/bin/clang" \
          -DCMAKE_PREFIX_PATH="$ROCM_PATH" \
          "${ck_cmake_opts}" \
          ..
    make -j"$(nproc)" install
    echo "Finished building Composable Kernel"
}

TEXT="ROCmSoftwarePlatform/composable_kernel@5f28614222bd590bc31d98838bc019e9c3a7ad45 -DGPU_TARGETS=\"gfx900;gfx906;gfx908;gfx90a;gfx1030;gfx1100;gfx1101;gfx1102\""

CK_COMMIT=$(echo "$TEXT" | awk '/composable_kernel/ {split($1, s, "@"); print s[2]}')
CK_CMAKE_OPTS=$(echo "$TEXT" | awk '/composable_kernel/ {for (i=1; i<=NF; i++) {if ($i ~ /^-D/) {printf "%s ", $i}}}')

build_miopen_ck "$CK_COMMIT" "$CK_CMAKE_OPTS"

Soon (might be a while) the build will fail with the following error:

clang-15: error: invalid target ID 'gfx900 --offload-arch=gfx906 --offload-arch=gfx908 --offload-arch=gfx90a --offload-arch=gfx1030 --offload-arch=gfx1100 --offload-arch=gfx1101 --offload-arch=gfx1102'; format is a processor name followed by an optional colon-delimited list of features followed by an enable/disable sign (e.g., 'gfx908:sramecc+:xnack-')

and by diagnosing the log, the single quotes look suspecious:

cmake -DBUILD_DEV=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=/opt/rocm/llvm/bin/clang++ -DCMAKE_C_COMPILER=/opt/rocm/llvm/bin/clang -DCMAKE_PREFIX_PATH= '-DGPU_TARGETS="gfx900;gfx906;gfx908;gfx90a;gfx1030;gfx1100;gfx1101;gfx1102" ' ..

if I go to the directory, and re-run cmake with

cmake -DBUILD_DEV=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=/opt/rocm/llvm/bin/clang++ -DCMAKE_C_COMPILER=/opt/rocm/llvm/bin/clang -DCMAKE_PREFIX_PATH= -DGPU_TARGETS="gfx900;gfx906;gfx908;gfx90a;gfx1030;gfx1100;gfx1101;gfx1102"  ..

then everything works as expected from there.

From the error above, it should have something to do with shell parameter expansion:

  • it should read as
    '--offload-arch=gfx900' '--offload-arch=gfx906' ...
but instead, the single quotes are messing up the **secondary** parsing process, and it reads as if it is:
    '--offload-arch=gfx900 --offload-arch=gfx906 ...'

As related to the minimal example above, if passing variable containing special characters as argument to a command does not automatically add a pair of single quote, we should not have this secondary parsing issues?

A few experiments so far:

  • as recommended by one of the users below, removing set -x does not resolve this issue unfortunately :(
  • maybe the minimal example above is still a good starting point? Is there a way to avoid adding single quotes? When are they added and how to remove? It's not part of the string itself unfortunately.

Workaround: So far a working workaround is to grep the string after -DGPU_TARGETS=, i.e. instead of cmake "$arg" we can make the following work cmake -DGPU_TARGETS="${subset_arg}". However, this is a workaround and the original issue still persists.

veritas
  • 196
  • 13
  • 2
    The single-quotes are added by `set -x` to indicate that the argument contains characters that *could* have some special meaning to the shell (i.e. `;`, which is normally a command delimiter), but that in this case the characters are just being passed literally as part of the argument (see [this question](https://stackoverflow.com/questions/6087494/bash-inserting-quotes-into-string-before-execution)). I'm not clear exactly what it's supposed to do (what command would run `cmake` the way you want it run here?), but all the non-double-quoted variable references look ripe for problems. – Gordon Davisson Apr 01 '23 at 08:03
  • @GordonDavisson correction from my experiments. No it doesn't resolve the issue with the original script. It seems that when removing `set -x` the single quotes are still passed together with the variable as arguments to cmake :( I wonder how to set up a minimal reproducible example in this case without set -x – veritas Apr 01 '23 at 08:47
  • 1
    Removing or adding the `set -x` shouldn't affect what arguments are passed to `cmake`. And, it is hard to believe, from the examples given in the question, that a _literal_ `'` is passed to `cmake` as a part of an argument. The problem must be somewhere else. – M. Nejat Aydin Apr 01 '23 at 09:14
  • What I'm asking for is a minimal reproducible example *of running `cmake` correctly*. Never mind the script, storing arguments in variables, etc, just give a command that runs `cmake` the way you want it run (e.g. `cmake -DCMAKE_ARG="arg1;arg2"` or whatever). – Gordon Davisson Apr 01 '23 at 09:22
  • As an aside, you may want to read [I'm trying to put a command in a variable, but the complex cases always fail!](https://mywiki.wooledge.org/BashFAQ/050). But, as I wrote in my previous comment, your problem must be somewhere else. – M. Nejat Aydin Apr 01 '23 at 10:14
  • 3
    As the bash tag you used instructs: "For shell scripts with syntax or other errors, please check them at https://shellcheck.net before posting them here". Fix the issues it tells you about then post the corrected code and let us know if you still have a problem. – Ed Morton Apr 01 '23 at 12:15

1 Answers1

0

You might need to add backslash doubled :

#!/bin/bash

set -x

run() {
    arg="$1"
    printf '%q\n' "$arg"
    cmake "$arg" 1>/dev/null 2>&1
}

TEXT="local -DCMAKE_ARG=arg1;arg2"

CMAKE_OPTS="${TEXT#local }" # Remove [local ]
CMAKE_OPTS="${CMAKE_OPTS//;/\\\\;}" # Add \ doubled
run "$CMAKE_OPTS"

# If four backslashes (\\\\) don't work, try two (\\)
Philippe
  • 20,025
  • 2
  • 23
  • 32
  • Bash treats escapes (and quotes) in variables as plain data, not shell syntax, so adding escapes/quotes to variables is only useful if they're going through an extra level of parsing for some reason -- and that doesn't seem to be the case here. – Gordon Davisson Apr 01 '23 at 17:01
  • @GordonDavisson cmake passes `CMAKE_ARG` to other commands, that's why OP had problems with `;` in the first place. – Philippe Apr 01 '23 at 18:41