# file      : tests/cc/modules/testscript
# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
# license   : MIT; see accompanying LICENSE file

crosstest = false
test.arguments = config.cxx="$recall($cxx.path)"

.include ../../common.test

+cat <<EOI >+build/bootstrap.build
using test
EOI

+cat <<EOI >=build/root.build
cxx.std = experimental

cxx.features.symexport = true

# Force modules.
#
cxx.features.modules = true

using cxx

# We forced modules but for VC we need at least 15u3 (19.11). So "unforce"
# them in this case.
#
if ($cxx.id == 'msvc' && $cxx.version.major == 19 && $cxx.version.minor < 11)
  cxx.features.modules = false

hxx{*}: extension = hxx
mxx{*}: extension = mxx
cxx{*}: extension = cxx

if ($cxx.target.class == 'windows')
  bmis{*}: cxx.poptions += '-DLIBFOO_EXPORT=__declspec(dllexport)'

exe{*}: test = true
EOI

# Determine if we have module support.
#
+$* noop <<EOI | set modules
print $cxx.features.modules
EOI

+$modules || exit

# Common source files that are symlinked in the test directories if used.
#
+cat <<EOI >=core.mxx
#ifndef LIBFOO_EXPORT
#  define LIBFOO_EXPORT
#endif

#if __cpp_modules >= 201704
export
#endif
module foo.core;

export LIBFOO_EXPORT int f (int);
EOI

+cat <<EOI >=core.cxx
module foo.core;
int f (int i) {return i - 1;}
EOI

+cat <<EOI >=driver.cxx
import foo.core;
int main (int argc, char*[]) {return f (argc);}
EOI

: bmi-combined
:
: Test combined interface/implementation unit specified as bmi{}.
:
cp ../core.mxx ./ && cat >+core.mxx <<EOI;
  int f (int i) {return i - 1;}
  EOI
ln -s ../driver.cxx ./;
$* test clean <<EOI
  exe{test}: cxx{driver} bmi{core}
  bmi{core}: mxx{core}
  EOI

: mxx-combined
:
: Test combined interface/implementation unit specified as mxx{}.
:
cp ../core.mxx ./ && cat >+core.mxx <<EOI;
  int f (int i) {return i - 1;}
  EOI
ln -s ../driver.cxx ./;
$* test clean <<EOI
  exe{test}: cxx{driver} mxx{core}
  EOI

: bmi-separate
:
: Test separate interface/implementation unit specified as bmi{}.
:
ln -s ../core.mxx ../core.cxx ../driver.cxx ./;
$* test clean <<EOI
  exe{test}: cxx{driver} {bmi cxx}{core}
  bmi{core}: mxx{core}
  EOI

: mxx-separate
:
: Test separate interface/implementation unit specified as mxx{}.
:
ln -s ../core.mxx ../core.cxx ../driver.cxx ./;
$* test clean <<EOI
  exe{test}: cxx{driver} {mxx cxx}{core}
  EOI

: name-match
:
: Test fuzzy/explicit match between module name and file name.
:
{
  # "Bad" match which we should better.
  #
  +cat <<EOI >=core.mxx
    #if __cpp_modules >= 201704
    export
    #endif
    module bar.core;
    EOI

  : separator
  :
  : Test separator equivalence.
  :
  ln -s ../../core.mxx foo-core.mxx;
  ln -s ../core.mxx ../../core.cxx ../../driver.cxx ./;
  $* test clean <'exe{test}: cxx{driver core} mxx{core foo-core}'

  : case
  :
  : Test case-insensitivity and case-change as a separator.
  :
  ln -s ../../core.mxx FooCore.mxx;
  ln -s ../core.mxx ../../core.cxx ../../driver.cxx ./;
  $* test clean <'exe{test}: cxx{driver core} mxx{core FooCore}'

  : dir
  :
  : Test subdirectory.
  :
  mkdir foo;
  ln -s ../../core.mxx foo/core.mxx;
  ln -s ../core.mxx ../../core.cxx ../../driver.cxx ./;
  $* test clean <'exe{test}: cxx{driver core} mxx{core} foo/mxx{core}'

  : explicit
  :
  : Explicit module name.
  :
  ln -s ../../core.mxx baz.mxx;
  ln -s ../core.mxx ../../core.cxx ../../driver.cxx ./;
  $* test clean <<EOO
    exe{test}: cxx{driver core} mxx{core baz}
    mxx{baz}@./: cc.module_name = foo.core
    EOO
}

: unresolved
:
ln -s ../driver.cxx ./;
$* test &*.d <'exe{test}: cxx{driver}' 2>>EOE != 0
  driver.cxx: error: unable to resolve module foo.core
  EOE

: misguessed
:
ln -s ../core.mxx ./;
cat <'import bar.core;' >=driver.cxx;
$* test &*.d &*.ii <'exe{test}: cxx{driver} mxx{core}' 2>>EOE != 0
  driver.cxx: error: failed to correctly guess module name from mxx{core}
    info: guessed: bar.core
    info: actual:  foo.core
    info: consider adjusting module interface file names or
    info: consider specifying module name with cc.module_name
  EOE

: library
:
: Test importing a module from a library.
:
ln -s ../core.mxx ../core.cxx ../driver.cxx ./;
$* test clean <<EOI
  ./: lib{foo} exe{test} # Full build.
  exe{test}: cxx{driver} lib{foo}
  lib{foo}: {mxx cxx}{core}
  EOI

: re-export
:
: Test module re-exporting (export import M;)
:
if ($cxx.id.type != "clang")
{
  +cat <<EOI >=base.mxx
    #if __cpp_modules >= 201704
    export
    #endif
    module foo.base;

    export import foo.core;
    EOI

  +cat <<EOI >=extra.mxx
    #ifndef LIBFOO_EXPORT
    #  define LIBFOO_EXPORT
    #endif

    #if __cpp_modules >= 201704
    export
    #endif
    module foo.extra;

    export
    {
      import foo.base;

      // VC appears to require dll-export of inline functions.
      //
      LIBFOO_EXPORT inline int g (int i) {return i != 0 ? i : -1;}
    }
    EOI

  +cat <<EOI >=foo.mxx
    #if __cpp_modules >= 201704
    export
    #endif
    module foo;

    export
    {
      import foo.core;
      import foo.base;
      import foo.extra;
    }
    EOI

  : basic
  :
  ln -s ../base.mxx ../../core.mxx ../../core.cxx ./;
  cat <<EOI >=driver.cxx;
    import foo.base;
    int main (int argc, char*[]) {return f (argc);}
    EOI
  $* test clean <'exe{test}: cxx{driver core} mxx{core base}'

  : recursive
  :
  ln -s ../base.mxx ../extra.mxx ../../core.mxx ../../core.cxx ./;
  cat <<EOI >=driver.cxx;
    import foo.extra;
    int main (int argc, char*[]) {return f (g (argc));}
    EOI
  $* test clean <'exe{test}: cxx{driver core} mxx{core base extra}'

  : duplicate
  :
  ln -s ../base.mxx ../extra.mxx ../foo.mxx ../../core.mxx ../../core.cxx ./;
  cat <<EOI >=driver.cxx;
    import foo;
    int main (int argc, char*[]) {return f (g (argc));}
    EOI
  $* test clean <'exe{test}: cxx{driver core} mxx{core base extra foo}'

  : library
  :
  ln -s ../base.mxx ../extra.mxx ../foo.mxx ../../core.mxx ../../core.cxx ./;
  cat <<EOI >=driver.cxx;
    import foo;
    int main (int argc, char*[]) {return f (g (argc));}
    EOI
  $* test clean <<EOI
    exe{test}: cxx{driver} mxx{foo} lib{foo}
    lib{foo}: mxx{core base extra} cxx{core}
    EOI
}

: import
:
: Test module import. Currently, all the implementation require access to the
: entire, recursively-explored list of BMIs.
:
{
  # Note: using VC export module fixup support.

  +cat <<EOI >=base.mxx
    export module foo.base;
    import foo.core;
    export int g (int i) {return f (i);}
    EOI

  +cat <<EOI >=extra.mxx
    export module foo.extra;
    import foo.base;
    export int h (int i) {return g (i);}
    EOI

  : basic
  :
  ln -s ../base.mxx ../../core.mxx ../../core.cxx ./;
  cat <<EOI >=driver.cxx;
    import foo.base;
    int main (int argc, char*[]) {return g (argc);}
    EOI
  $* test clean <'exe{test}: cxx{driver core} mxx{core base}'

  : recursive
  :
  ln -s ../base.mxx ../extra.mxx ../../core.mxx ../../core.cxx ./;
  cat <<EOI >=driver.cxx;
    import foo.extra;
    int main (int argc, char*[]) {return h (argc);}
    EOI
  $* test clean <'exe{test}: cxx{driver core} mxx{core base extra}'
}

: resolve-change
:
: Test detection of module name to BMI resolution change.
:
ln -s ../core.mxx ../core.cxx ../driver.cxx ./;
cat <<EOI >=foo-core.mxx;
  #if __cpp_modules >= 201704
  export
  #endif
  module foo.core;
  export inline int f (int i) {return i - 2;}
  EOI
$* update <<EOI;
  ./: exe{test} bmie{foo-core}
  exe{test}: cxx{driver} {mxx cxx}{core}
  bmie{foo-core}: mxx{foo-core}
  EOI
$* test --verbose 1 <<EOI 2>>EOE;
  exe{test}: cxx{driver} {mxx}{foo-core}
  exe{test}: test.arguments = two
  EOI
  c++ cxx{driver}
  ld exe{test}
  test exe{test}
  EOE
$* test clean <<EOI
  ./: exe{test} bmie{foo-core}
  exe{test}: cxx{driver} {mxx cxx}{core}
  bmie{foo-core}: mxx{foo-core}
  EOI

: symexport
:
: Test the __symexport feature.
:
cat <<EOI >=core.mxx;
  #if __cpp_modules >= 201704
  export
  #endif
  module foo.core;

  export __symexport int f (int);

  __symexport int g_impl (int i) {return i - 1;}
  export __symexport inline int g (int i) {return g_impl (i);}
  EOI
ln -s ../core.cxx core-f.cxx;
cat <<EOI >=core-g.cxx;
  module foo.core;
  int g_impl (int i) {return i - 1;}
  EOI
cat <<EOI >=driver.cxx;
  import foo.core;
  int main (int argc, char*[]) {return f (argc) + g (argc);}
  EOI
$* test clean <<EOI
  ./: lib{foo} exe{test} # Full build.
  exe{test}: cxx{driver} lib{foo}
  lib{foo}: mxx{core} cxx{core-f} # core-g @@ VC
  EOI

: export-fixup
:
: Test removing the export keyword for VC compatibility.
:
ln -s ../core.cxx ../driver.cxx ./;
cat <<EOI >=core.mxx;
  export module foo.core;
  export int f (int);
  EOI
$* test <'exe{test}: cxx{driver core} mxx{core}';
touch core.mxx; # Test use of cached .ii's.
$* test clean <'exe{test}: cxx{driver core} mxx{core}'