# file      : tests/recipe/buildscript/testscript
# license   : MIT; see accompanying LICENSE file

posix = ($cxx.target.class != 'windows')

+mkdir build
+cat <<EOI >=build/bootstrap.build
  project = test
  amalgamation =
  subprojects =

  using config
  using test
  EOI

+cat <<EOI >=build/root.build
  EOI

: update
:
{
  : success
  :
  {
    echo 'bar' >=bar;

    cat <<EOI >=buildfile;
      foo: bar
      {{
        cp $path($<) $path($>)
      }}
      EOI

    $* 2>'cp file{foo}';

    cat <<<foo >'bar';

    # While at it, make sure there is no rebuild.
    #
    $* 2>/'info: dir{./} is up to date';

    $* clean 2>-
  }

  : error
  :
  : Test that the target file is removed on error and is created on subsequent
  : successful update.
  :
  {
    echo 'bar' >=bar;

    cat <<EOI >=buildfile;
      foo: bar
      {{
        diag concat $<

        t = $path($>)
        p = $path($<)

        cp $p $t

        cat $(p).baz >>$t
      }}
      EOI

    $* 2>>~%EOE% != 0;
      concat file{bar.}
      %cat: unable to print '.+bar.baz': .+%
      buildfile:10:3: error: cat exited with code 1
      %.+
      EOE

    test -f foo != 0;

    echo 'baz' >=bar.baz;

    $* 2>'concat file{bar.}';

    cat <<<foo >>EOO;
      bar
      baz
      EOO

    $* clean 2>-
  }

  : mutual-redirects
  :
  {
    echo 'bar' >=bar;

    cat <<EOI >=buildfile;
      foo: bar
      % [diag=cp]
      {{
        echo 'copying' 2>&1
        cp $path($<) $path($>)
      }}
      EOI

    $* 2>>~%EOE% != 0;
      cp file{foo}
      buildfile:4:3: error: stdout and stderr redirected to each other
      %.+
      EOE

    $* clean 2>-
  }

  : untracked-var
  :
  {
    cat <<EOI >=buildfile;
      a = a
      b = b
      foo:
      {{
        x = true
        echo "$($x ? a : b)" >$path($>)
      }}
      EOI

    $* 2>>~%EOE% != 0;
      echo file{foo}
      buildfile:6:10: error: use of untracked variable 'a'
        info: use the 'depdb' builtin to manually track it
      %.+
      EOE

    $* clean 2>-
  }

  : export
  :
  if $posix
  {
    cat <<EOI >=bar;
      #!/bin/sh
      echo "$message"
      EOI

    cat <<EOI >=buildfile;
      exe{foo}: bar
      {{
        cp $path($<) $path($>)
      }}
      % test
      {{
        diag test $>
        export message=text1
        $> >>>?'text1'
        env message=text2 -- $> >>>?'text2'
      }}
      EOI

    $* test 2>>EOE;
      cp exe{foo}
      test exe{foo.}
      EOE

    $* clean 2>-
  }

  : depdb
  :
  {
    : track-var-auto
    :
    {
      echo 'bar' >=bar;

      cat <<EOI >=buildfile;
        s = $process.run(cat bar)
        foo:
        {{
          echo "$s" >$path($>)
        }}
        EOI

      $* 2>'echo file{foo}';
      cat <<<foo >'bar';

      $* 2>/'info: dir{./} is up to date';

      echo 'baz' >=bar;

      $* 2>'echo file{foo}';
      cat <<<foo >'baz';

      $* clean 2>-
    }

    : track-var-manual
    :
    {
      echo 'bar' >=bar;
      echo 'baz' >=baz;

      cat <<EOI >=buildfile;
        a = $process.run(cat baz)
        foo: bar
        {{
          depdb hash "$a"
          diag compose $>

          cp $path($<) $path($>)

          x = true
          echo "$($x ? a : b)" >>$path($>)
        }}
        EOI

      $* 2>'compose file{foo.}';

      cat <<<foo >>EOO;
        bar
        baz
        EOO

      $* 2>/'info: dir{./} is up to date';

      # Make sure that on filesystems with a low file timestamps resolution
      # (for example HFS+) the file is considered as changed.
      #
      sleep 1;

      echo 'BAR' >=bar;

      $* 2>'compose file{foo.}';

      cat <<<foo >>EOO;
        BAR
        baz
        EOO

      $* 2>/'info: dir{./} is up to date';

      echo 'BAZ' >=baz;

      $* 2>'compose file{foo.}';

      cat <<<foo >>EOO;
        BAR
        BAZ
        EOO

      $* 2>/'info: dir{./} is up to date';

      $* clean 2>-
    }

    : preamble
    :
    {
      : valid
      :
      {
        echo 'bar' >=bar;

        cat <<EOI >=buildfile;
          s = $process.run(cat bar)
          foo:
          {{
            depdb clear

            s1 = 'abc'
            s2 = 'xyz'

            if echo "$s" >>>? 'bar'
              v = "$s1"
            else
              echo "$s2" | set v
            end

            depdb string "$v"

            echo "$v" >$path($>)
          }}
          EOI

        $* 2>'echo file{foo}';
        cat <<<foo >'abc';

        $* 2>/'info: dir{./} is up to date';

        echo 'baz' >=bar;
        $* 2>'echo file{foo}';
        cat <<<foo >'xyz';

        $* clean 2>-
      }

      : invalid
      :
      {
        cat <<EOI >=buildfile;
          foo:
          {{
            v = 'abc'
            echo "$v" >$path($>)
            depdb string "$v"
          }}
          EOI

        $* 2>>~%EOE% != 0;
          buildfile:4:3: error: disallowed command in depdb preamble
            info: only variable assignments are allowed in depdb preamble
            buildfile:5:3: info: depdb preamble ends here
          %.+
          EOE

        $* clean 2>-
      }

      : temp-dir
      :
      {
        cat <<EOI >=buildfile;
          foo:
          {{
            touch $~/f | set dummy

            if test -f $~/f
              v = "yes"
            else
              v = "no"
            end

            depdb string "$v"
            diag echo $>

            test -f $~/f
            echo "$v" >$path($>)
          }}
          EOI

        $* 2>'echo file{foo.}';

        $* clean 2>-
      }
    }

    : string
    :
    {
      echo 'bar' >=bar;

      cat <<EOI >=buildfile;
        s = $process.run(cat bar)
        foo:
        {{
          depdb clear
          depdb string "$s"
          echo "$s" >$path($>)
        }}
        EOI

      $* 2>'echo file{foo}';
      cat <<<foo >'bar';

      $* 2>/'info: dir{./} is up to date';

      echo 'baz' >=bar;

      $* 2>'echo file{foo}';
      cat <<<foo >'baz';

      $* clean 2>-
    }

    : hash
    :
    {
      echo 'bar' >=bar;

      cat <<EOI >=buildfile;
        s = $process.run(cat bar)
        foo:
        {{
          depdb clear
          depdb hash "$s"
          echo "$s" >$path($>)
        }}
        EOI

      $* 2>'echo file{foo}';
      cat <<<foo >'bar';

      $* 2>/'info: dir{./} is up to date';

      echo 'baz' >=bar;

      $* 2>'echo file{foo}';
      cat <<<foo >'baz';

      $* clean 2>-
    }

    : env
    :
    {
      : invalid
      :
      {
        cat <<EOI >=buildfile;
          foo:
          {{
            s = $getenv(FOO)

            depdb clear
            depdb env FOO=bar
            echo "$s" >$path($>)
          }}
          EOI

        $* 2>>/EOE != 0;
          buildfile:6:3: error: invalid 'depdb env' argument: invalid variable name 'FOO=bar': contains '='
            info: while updating file{foo}
            info: while updating dir{./}
          info: failed to update dir{./}
          EOE

        $* clean 2>-
      }

      : valid
      :
      {
        cat <<EOI >=buildfile;
          foo:
          {{
            s = $getenv(FOO)

            depdb clear
            depdb env FOO
            echo "$s" >$path($>)
          }}
          EOI

        export FOO=foo;

        $* 2>'echo file{foo}';
        cat <<<foo >'foo';

        $* 2>/'info: dir{./} is up to date';

        export FOO=bar;

        $* 2>'echo file{foo}';
        cat <<<foo >'bar';

        export -u FOO;

        $* 2>'echo file{foo}';
        cat <<<foo >'';

        $* clean 2>-
      }
    }

    : dyndep
    :
    {
      : normal
      :
      {
	cat <<EOI >=bar.h;
          bar
          EOI

        cat <<EOI >=buildfile;
          define h: file
          h{*}: extension = h

          ./: h{foo baz}

          h{foo}:
          {{
            # Note that strictly speaking we should return $out_base/baz.h
            # on the second invocation (since it now exists). But our dyndep
            # machinery skips the entry which it has already seen, so this
            # works for now.
            #
            depdb dyndep "-I$out_base" --what=header --default-type=h -- \
              echo "$out_base/foo.h: $src_base/bar.h baz.h"

            diag gen $>

            cat $path($<) >$path($>)
          }}

          h{baz}:
          {{
            diag gen $>
            echo baz >$path($>)
          }}
          EOI

	$* 2>>EOE;
          gen h{baz.h}
          gen h{foo.h}
          EOE

	cat foo.h >>EOO;
	  bar
	  baz
	  EOO

	$* clean 2>-
      }

      : byproduct
      :
      {
	cat <<EOI >=bar.h;
          bar
          EOI

        cat <<EOI >=buildfile;
          define h: file
          h{*}: extension = h

          h{foo}: h{baz}
          {{
            o = $path($>)
            t = $path($>).t

            depdb dyndep --byproduct --what=header --default-type=h --file $t

            diag gen $>
            cat $src_base/bar.h $out_base/baz.h >$o
            echo "$out_base/foo.h: $src_base/bar.h $out_base/baz.h" >$t
          }}

          h{baz}:
          {{
            diag gen $>
            echo baz >$path($>)
          }}
          EOI

	$* 2>>EOE;
          gen h{baz.h}
          gen h{foo.h}
          EOE

	cat foo.h >>EOO;
	  bar
	  baz
	  EOO

	$* clean 2>-
      }
    }
  }
}

: clean
:
{
  echo 'bar' >=bar;

  cat <<EOI >=buildfile;
    foo: bar
    {{
      cp $path($<) $path($>)
    }}
    % [diag=clean] clean
    {{
      t = $path($>)
      rm $t $(t).d
    }}
    EOI

  $* 2>-;

  # Rely on the cleanup machinery to verify that the build output files are
  # removed.
  #
  $* clean 2>'clean file{foo}'
}

: test
:
{
  : success
  :
  {
    echo 'bar' >=bar;

    cat <<EOI >=buildfile;
      foo: bar
      {{
        cp $path($<) $path($>)
      }}
      % [diag=test] test
      {{
        cat <$path($>) >?$path($<)
      }}
      EOI

    $* test 2>>EOE;
      cp file{foo}
      test file{foo}
      EOE

    $* clean 2>-
  }

  : depdb
  :
  {
    echo 'bar' >=bar;

    cat <<EOI >=buildfile;
      foo: bar
      {{
        cp $path($<) $path($>)
      }}
      % [diag=test] test
      {{
        depdb clear
        cat <$path($>) >?$path($<)
      }}
      EOI

    $* test 2>>EOE != 0
      buildfile:7:3: error: 'depdb' builtin cannot be used to perform test
      EOE
  }

  : runner
  :
  if $posix
  {
    echo 'bar' >=bar;

    cat <<EOI >=run;
      #!/bin/sh
      if test "$1" = "--trace"; then
        shift
        echo "$*"
      fi
      "$@"
      EOI

    chmod u+x run;

    cat <<EOI >=buildfile;
      foo: bar
      {{
        cp $path($<) $path($>)
      }}
      % [diag=test] test
      {{
        if ($test.runner.path != [null])
          $test.runner.path $test.runner.options cat <$path($>)
        else
          cat <$path($>)
        end
      }}
      EOI

    $* test 2>>EOE;
      cp file{foo}
      test file{foo}
      bar
      EOE

    $* test config.test.runner="./run --trace" 2>>EOE;
      test file{foo}
      cat
      bar
      EOE

    $* clean 2>-
  }
}

: diff-label
:
{
  echo 'bar' >=bar;

  cat <<EOI >=buildfile;
    foo: bar
    {{
      echo 'baz' >? $path($<)
    }}
    EOI

  $* 2>>/~%EOE% != 0;
    %.+
    %--- .+/bar%
    +++ stdout
    %.+
    EOE

  $* clean 2>-
}

: timeout
:
if $posix
{
  : update
  :
  {
    : expired
    :
    {
      echo 'bar' >=bar;

      cat <<EOI >=buildfile;
        foo: bar
        % [diag=update]
        {{
          cp $path($<) $path($>)
          timeout 1
          ^sleep 5
        }}
        EOI

      $* 2>>~%EOE% != 0;
        update file{foo}
        buildfile:6:3: error: ^sleep terminated: execution timeout expired
          info: while updating file{foo}
        %.+
        EOE

      $* clean 2>-
    }

    : successful-timeout
    :
    {
      echo 'bar' >=bar;

      cat <<EOI >=buildfile;
        foo: bar
        % [diag=update]
        {{
          cp $path($<) $path($>)
          timeout --success 1
          ^sleep 5
        }}
        EOI

      $* 2>>EOE;
        update file{foo}
        EOE

      $* clean 2>-
    }
  }

  : test
  :
  {
    : expired
    :
    {
      echo 'bar' >=bar;

      cat <<EOI >=buildfile;
        foo: bar
        {{
          cp $path($<) $path($>)
        }}
        % [diag=test] test
        {{
          ^sleep 5
        }}
        EOI

      $* test config.test.timeout=1 2>>~%EOE% != 0;
        cp file{foo}
        test file{foo}
        buildfile:7:3: error: ^sleep terminated: execution timeout expired
          info: while testing file{foo}
        %.+
        EOE

      $* test config.test.timeout=/1 2>>~%EOE% != 0;
        test file{foo}
        buildfile:7:3: error: ^sleep terminated: execution timeout expired
          info: while testing file{foo}
        %.+
        EOE

      $* clean 2>-
    }

    : not-expired
    :
    {
      echo 'bar' >=bar;

      cat <<EOI >=buildfile;
        foo: bar
        % [diag=cp]
        {{
          ^sleep 4
          cp $path($<) $path($>)
        }}
        % [diag=test] test
        {{
          ^sleep 1
        }}
        EOI

      $* test config.test.timeout=3 2>>EOE;
        cp file{foo}
        test file{foo}
        EOE

      $* clean 2>-
    }
  }
}

# @@ TODO: test $1 when implemented.
#
: rule
:
{
  cat <<EOI >=buildfile;
    alias{far}: alias{bar}
    alias{bar}:

    alias{~'/f(.+)/'}: alias{~'/b\1/'}
    {{
      diag $< $>
    }}
    EOI

  $* 2>>EOE
    alias{bar} alias{far}
    EOE
}