# file      : tests/sync.testscript
# license   : MIT; see accompanying LICENSE file

.include common.testscript

config_cxx = [cmdline] cc config.cxx=$quote($recall($cxx.path) $cxx.config.mode, true)

new    += 2>!
init   += 2>!
config += 2>!
status += --all --recursive -d prj
deinit += -d prj

: single-pkg-cfg
:
{
  $new -C @cfg prj $config_cxx &prj/*** &prj-cfg/***

  $new -t lib libprj &libprj/***

  cat <<EOI >+prj/repositories.manifest
    :
    role: prerequisite
    location: ../libprj
    type: dir
    EOI

  cat <<EOI >+prj/manifest
    depends: libprj
    EOI

  sed -i -e 's/^(#import .+)$/import libs += libprj%lib{prj}/' \
      prj/prj/buildfile

  cat <<EOI >=prj/prj/prj.cxx
    #include <iostream>

    #include <libprj/prj.hxx>

    using namespace std;

    int main (int argc, char* argv[])
    {
      if (argc < 2)
      {
        cerr << "error: missing name" << endl;
        return 1;
      }

      prj::say_hello (cout, argv[1]);
    }
    EOI

  $* -d prj 2>>/~%EOE%
    %fetching dir:.+/libprj \(prerequisite of dir:.+/prj\)%
    synchronizing:
    %  new libprj.+ \(required by prj\)%
    %  upgrade prj.+19700101000000#1%
    EOE

  $status >>~%EOO%
    prj configured 0.1.0-a.0.19700101000000#1
    %  libprj configured 0.+%
    EOO

  $build prj/ 2>>~%EOE%
    %(mkdir|version|c\+\+|ld|ln) .+%{8}
    EOE

  $build 'clean:' prj/ 2>>~%EOE%
    %(rm|rmdir) .+%{3}
    EOE

  $deinit 2>>/"EOE"
    deinitializing in project $~/prj/
    synchronizing:
      drop prj
      drop libprj
    EOE
}

: multi-pkg-cfg
:
{
  $new -t empty prj &prj/***

  $new --package pkg1 -d prj
  $new --package pkg2 -d prj

  init += -d prj

  $init -C @cfg1 $config_cxx &prj-cfg1/***
  $init -C @cfg2 $config_cxx &prj-cfg2/***

  $new -t lib libprj &libprj/***

  cat <<EOI >+prj/repositories.manifest
    :
    role: prerequisite
    location: ../libprj
    type: dir
    EOI

  cat <<EOI >+prj/pkg1/manifest
    depends: libprj
    EOI

  sed -i -e 's/^(#import .+)$/import libs += libprj%lib{prj}/' \
      prj/pkg1/pkg1/buildfile

  cat <<EOI >=prj/pkg1/pkg1/pkg1.cxx
    #include <iostream>

    #include <libprj/prj.hxx>

    using namespace std;

    int main (int argc, char* argv[])
    {
      if (argc < 2)
      {
        cerr << "error: missing name" << endl;
        return 1;
      }

      prj::say_hello (cout, argv[1]);
    }
    EOI

  cat <<EOI >+prj/pkg2/manifest
    tags: c++
    EOI

  # Sync the default (cfg1) configuration (via the package directory).
  #
  $* -d prj/pkg2 2>>/~%EOE%
    %fetching dir:.+/libprj \(prerequisite of dir:.+/prj\)%
    synchronizing:
    %  new libprj.+ \(required by pkg1\)%
    %  upgrade pkg1.+19700101000000#1%
    %  upgrade pkg2.+19700101000000#1%
    EOE

  $status >>~%EOO% 2>>/~%EOE% # Note: implicitly fetches into cfg2.
    in configuration @cfg1:
    pkg1 configured 0.1.0-a.0.19700101000000#1
    %  libprj configured 0.+%
    pkg2 configured 0.1.0-a.0.19700101000000#1

    in configuration @cfg2:
    pkg1 configured 0.1.0-a.0.19700101000000 available 0.1.0-a.0.19700101000000#1
    pkg2 configured 0.1.0-a.0.19700101000000 available 0.1.0-a.0.19700101000000#1
    EOO
    %fetching dir:.+/libprj \(prerequisite of dir:.+/prj\)%
    EOE

  # Sync all configurations (via the project directory).
  #
  $* --all -d prj 2>>~%EOE%
    in configuration @cfg1:

    in configuration @cfg2:
    synchronizing:
    %  new libprj.+ \(required by pkg1\)%
      upgrade pkg1/0.1.0-a.0.19700101000000#1
      upgrade pkg2/0.1.0-a.0.19700101000000#1
    EOE

  $status >>~%EOE%
    in configuration @cfg1:
    pkg1 configured 0.1.0-a.0.19700101000000#1
    %  libprj configured 0.+%
    pkg2 configured 0.1.0-a.0.19700101000000#1

    in configuration @cfg2:
    pkg1 configured 0.1.0-a.0.19700101000000#1
    %  libprj configured 0.+%
    pkg2 configured 0.1.0-a.0.19700101000000#1
    EOE

  $build prj/pkg1/ 2>>~%EOE%
    %(mkdir|version|c\+\+|ld|ln) .+%{8}
    EOE

  $build prj/pkg2/ 2>>~%EOE%
    %(mkdir|c\+\+|ld|ln) .+%{4}
    EOE

  $build 'clean:' prj/pkg1/ 2>>~%EOE%
    %(rm|rmdir) .+%{3}
    EOE

  $build 'clean:' prj/pkg2/ 2>>~%EOE%
    %(rm|rmdir) .+%{3}
    EOE

  $deinit 2>>/"EOE"
    deinitializing in project $~/prj/
    deinitializing package pkg1
    deinitializing package pkg2
    synchronizing:
      drop pkg1
      drop libprj
      drop pkg2
    EOE
}

: pkg-by-name
:
{
  $new -t empty prj &prj/***

  $new --package pkg1 -d prj
  $new --package pkg2 -d prj

  init += -d prj

  $init -C @cfg $config_cxx &prj-cfg/***

  $new -t lib libprj &libprj/***

  cat <<EOI >+prj/repositories.manifest
    :
    role: prerequisite
    location: ../libprj
    type: dir
    EOI

  cat <<EOI >+prj/pkg1/manifest
    depends: libprj
    EOI

  cat <<EOI >+prj/pkg2/manifest
    tags: c++
    EOI

  $* pkg1 -d prj/pkg2 ?libprj 2>>~%EOE%
    %.+
    synchronizing:
      new libprj/0.1.0-a.0.19700101000000 (required by pkg1)
      upgrade pkg1/0.1.0-a.0.19700101000000#1
      upgrade pkg2/0.1.0-a.0.19700101000000#1
    EOE

  sed -i -e 's/^(version:).+$/\1 1.0.0/' libprj/manifest

  $* libprj -f -d prj 2>>~%EOE%
    %.+
    synchronizing:
      upgrade libprj/1.0.0
      reconfigure pkg1/0.1.0-a.0.19700101000000#1
    EOE

  $* libprj pkg1 -d prj 2>>EOE != 0
    error: initialized package pkg1 specified with dependency package libprj
    EOE

  $deinit 2>>/"EOE"
    deinitializing in project $~/prj/
    deinitializing package pkg1
    deinitializing package pkg2
    synchronizing:
      drop pkg1
      drop libprj
      drop pkg2
    EOE
}

: config-vars
:
{
  $new prj &prj/***

  cat <<EOI >+prj/build/root.build
    config [bool] config.prj.develop ?= false
    config [bool] config.prj.extra ?= true

    text "develop=$config.prj.develop"
    text "extra=$config.prj.extra"
    EOI

  $init -d prj -C @cfg $config_cxx &prj-cfg/*** 2>>~/EOE/
    /.*/+
    /.*: develop=true/
    /.*: extra=true/
    EOE

  # Reconfigure.
  #
  $* -d prj config.prj.develop=false config.prj.extra=false 2>>~/EOE/
    /.*/+
    /.*: develop=false/
    /.*: extra=false/
    EOE

  # Make sure the configuration is by default preserved on reconfiguration.
  #
  cat <<EOI >+prj/manifest
    tags: c++
    EOI

  $* -d prj config.prj.develop=false config.prj.extra=false 2>>~/EOE/
    /.*/+
    /.*: develop=false/
    /.*: extra=false/
    EOE

  # Make sure --disfigure causes a from-scratch reconfiguration.
  #
  $* -d prj --disfigure config.prj.extra=false 2>>~/EOE/
    /.*/+
    /.*: develop=true/
    /.*: extra=false/
    EOE

  # Make sure --disfigure alone triggers reconfiguration.
  #
  $* -d prj --disfigure 2>>~/EOE/
    /.*/+
    /.*: develop=true/
    /.*: extra=true/
    EOE
}

: dependency-config
:
{{
  +$new -t lib libfoo &libfoo/***
  +$new -t lib libfix &libfix/***
  +$new        bar    &bar/***

  +cat <<EOI >+libfoo/repositories.manifest
    :
    role: prerequisite
    location: ../bar
    type: dir
    EOI

  +cat <<EOI >+libfoo/manifest
    depends: * bar
    EOI

  +cat <<EOI >+libfoo/buildfile
    import bar = bar%exe{bar}
    EOI

  +cat <<EOI >+libfix/repositories.manifest
    :
    role: prerequisite
    location: ../bar
    type: dir
    EOI

  +cat <<EOI >+libfix/manifest
    depends: * bar
    EOI

  +cat <<EOI >+libfix/buildfile
    import bar = bar%exe{bar}
    EOI

  +cat <<EOI >=bar/build/export.build
    export $out_root/$import.target
    EOI

  : create
  {
    cp -rp ../libfoo ./
    cp -rp ../libfix ./
    cp -rp ../bar    ./

    $init -d libfoo -C            @cfg --no-sync $config_cxx &libfoo-cfg/***
    $init -d libfix -A libfoo-cfg @cfg --no-sync

    $* -d libfoo 2>>~%EOE% != 0
      %fetching dir:.+bar \(prerequisite of dir:.+libfoo\)%
      error: unable to find configuration of host type for build-time dependency
        info: run sync command explicitly
      %info: while searching for configuration for build-time dependency bar of package libfoo/.+ \[.+libfoo-cfg.\]%
      %info: while synchronizing configuration .+libfoo-cfg.%
      EOE

    $* -d libfoo --create-host-config &libfoo-host/*** &libf??/**/bootstrap/*** 2>>~%EOE%
      synchronizing:
      %  new bar/.+ \[.+libfoo-host.\] \(required by libfix, libfoo\)%
      %  new libfoo/.+%
      %  new libfix/.+%
      EOE
  }

  : find
  :
  {
    cp -rp ../libfoo ./
    cp -rp ../libfix ./
    cp -rp ../bar    ./

    $config create -d libfoo                    @cfg  libfoo-cfg -- &libfoo-cfg/***
    $config create -d libfoo --config-type host @host host       -- &host/***

    $config link -d libfoo @cfg @host

    # Convert specific warnings to infos as we expect them to appear. This, in
    # particular, prevents bbot workers to set task result status to warning.
    #
    $init -d libfix -A libfoo-cfg @cfg --no-sync 2>&1 | \
    sed -e 's/warning: (added configuration @cfg already linked .*)/info: \1/' >&2 2>!

    # While at it, test synchronizing via the init command.
    #
    $init -d libfoo @cfg &libf??/**/bootstrap/*** 2>>~%EOE%
      %initializing in project .+libfoo.%
      %fetching dir:.+bar \(prerequisite of dir:.+libfoo\)%
      synchronizing:
      %  new bar/.+ \[.+host.\] \(required by libfix, libfoo\)%
      %  new libfoo/.+%
      %  new libfix/.+%
      EOE
  }

  : re-associate
  :
  : Test that while failing, bdep-init re-associates the automatically created
  : build-time dependency configurations.
  :
  {
    cp -rp ../libfoo ./
    cp -rp ../libfix ./
    cp -rp ../bar    ./

    cat <<EOI >+bar/manifest
      depends: * libbuild2-baz
      EOI

    $config create -d libfoo @cfg libfoo-cfg -- &libfoo-cfg/***

    # Convert specific errors to infos as we expect them to appear. Not doing
    # so, makes bbot logs quite confusing.
    #
    $init -d libfoo --create-host-config --create-build2-config @cfg &libfoo-host/*** &libfoo-build2/*** 2>&1 != 0 | \
    sed -e 's/error: (unknown dependency libbuild2-baz .*)/info: \1/' >&2 2>!

    $config list -d libfoo >>~%EOO%
      %(@cfg|@host|@build2).+%{3}
      EOO
  }
}}

: failure-preserve-user-config
:
{
  $new -t empty -C @cfg prj $config_cxx &prj/*** &prj-cfg/***

  $new --package           pkg -d prj
  $new --package -t lib libpkg -d prj

  echo 'depends: libpkg' >+prj/pkg/manifest
  sed -i -e 's/^#import.+$/import libs += libpkg%lib{pkg}/' prj/pkg/pkg/buildfile

  echo 'config [bool] config.pkg.feature ?= false' >+prj/pkg/build/root.build
  echo 'config [bool] config.libpkg.feature ?= false' >+prj/libpkg/build/root.build

  $init -d prj/ @cfg config.pkg.feature=true config.libpkg.feature=true 2>>~%EOE%
    %initializing in project .+failure-preserve-user-config.+%
    initializing package pkg
    initializing package libpkg
    synchronizing:
      new libpkg/0.1.0-a.0.19700101000000
        config.pkg.feature=true (user configuration)
        config.libpkg.feature=true (user configuration)
      new pkg/0.1.0-a.0.19700101000000
        config.pkg.feature=true (user configuration)
        config.libpkg.feature=true (user configuration)
    %.*
    EOE

  sed -n -e 's/config.pkg.feature = (.+)/\1/p' \
      prj-cfg/pkg/build/config.build >'true'

  sed -n -e 's/src_root = (.+)/\1/p' \
      prj-cfg/pkg/build/bootstrap/src-root.build >/~"%.+/prj/pkg/'?%"

  sed -n -e 's/config.libpkg.feature = (.+)/\1/p' \
      prj-cfg/libpkg/build/config.build >'true'

  sed -n -e 's/src_root = (.+)/\1/p' \
      prj-cfg/libpkg/build/bootstrap/src-root.build >/~"%.+/prj/libpkg/'?%"

  sed -n -e 's/src_root = (.+)/\1/p' \
      prj-cfg/libpkg/tests/build/bootstrap/src-root.build >/~"%.+/prj/libpkg/tests/'?%"

  mkdir --no-cleanup prj/libpkg/stray
  echo '' >+prj/libpkg/manifest

  $build prj/pkg/ 2>>~%EOE% != 0
    %synchronizing .*prj-cfg.:%
      upgrade libpkg/0.1.0-a.0.19700101000000#1
        config.libpkg.feature=true (user configuration)
      reconfigure pkg/0.1.0-a.0.19700101000000
        config.pkg.feature=true (user configuration)
    %error: no explicit target for .+stray.+%
    info: while configuring libpkg
    %.*
    EOE

  sed -n -e 's/config.pkg.feature = (.+)/\1/p' \
      prj-cfg/pkg/build/config.build >'true'

  sed -n -e 's/src_root = (.+)/\1/p' \
      prj-cfg/pkg/build/bootstrap/src-root.build >/~"%.+/prj/pkg/'?%"

  sed -n -e 's/config.libpkg.feature = (.+)/\1/p' \
      prj-cfg/libpkg/build/config.build >'true'

  sed -n -e 's/src_root = (.+)/\1/p' \
      prj-cfg/libpkg/build/bootstrap/src-root.build >/~"%.+/prj/libpkg/'?%"

  sed -n -e 's/src_root = (.+)/\1/p' \
      prj-cfg/libpkg/tests/build/bootstrap/src-root.build >/~"%.+/prj/libpkg/tests/'?%"

  rmdir prj/libpkg/stray

  $build prj/pkg/ 2>>~%EOE%
    %synchronizing .*prj-cfg.:%
      update libpkg/0.1.0-a.0.19700101000000#1
      update pkg/0.1.0-a.0.19700101000000
    %(mkdir|version|c\+\+|ld|ln) .+%{8}
    EOE

  sed -n -e 's/config.pkg.feature = (.+)/\1/p' \
      prj-cfg/pkg/build/config.build >'true'

  sed -n -e 's/src_root = (.+)/\1/p' \
      prj-cfg/pkg/build/bootstrap/src-root.build >/~"%.+/prj/pkg/'?%"

  sed -n -e 's/config.libpkg.feature = (.+)/\1/p' \
      prj-cfg/libpkg/build/config.build >'true'

  sed -n -e 's/src_root = (.+)/\1/p' \
      prj-cfg/libpkg/build/bootstrap/src-root.build >/~"%.+/prj/libpkg/'?%"

  sed -n -e 's/src_root = (.+)/\1/p' \
      prj-cfg/libpkg/tests/build/bootstrap/src-root.build >/~"%.+/prj/libpkg/tests/'?%"

  $deinit 2>>/"EOE"
    deinitializing in project $~/prj/
    deinitializing package pkg
    deinitializing package libpkg
    synchronizing:
      drop pkg
      drop libpkg
    EOE
}
