diff --git a/.github/actions/linux-setup-env/action.yml b/.github/actions/linux-setup-env/action.yml index 1d11cdcfd5..71cdd6d38c 100644 --- a/.github/actions/linux-setup-env/action.yml +++ b/.github/actions/linux-setup-env/action.yml @@ -7,6 +7,8 @@ inputs: java-version: description: "Java version to use in tests" default: "8" + llvm-version: + description: "LLVM toolchain version" runs: using: "composite" steps: @@ -33,6 +35,14 @@ runs: sudo apt-get update sudo apt-get install libgc-dev + - name: Install explicit LLVM toolchain + shell: bash + if: ${{ inputs.llvm-version != '' }} + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + yes "" | sudo ./llvm.sh ${{ inputs.llvm-version }} + # Loads cache with dependencies created in test-tools job - name: Cache dependencies uses: actions/cache@v3 diff --git a/.github/actions/macos-setup-env/action.yml b/.github/actions/macos-setup-env/action.yml index 6ca2bc0688..262546c2e7 100644 --- a/.github/actions/macos-setup-env/action.yml +++ b/.github/actions/macos-setup-env/action.yml @@ -7,6 +7,8 @@ inputs: java-version: description: "Java version to use in tests" default: "8" + gc: + description: "Garbage collector used, might require installing dependencies" runs: using: "composite" steps: @@ -26,6 +28,11 @@ runs: echo "binary-version=3" >> $GITHUB_ENV echo "project-version=3" >> $GITHUB_ENV fi + + - name: Install dependencies + shell: bash + if: ${{ startsWith(inputs.gc, 'boehm') }} + run: brew install bdw-gc # Loads cache with dependencies created in test-tools job - name: Cache dependencies diff --git a/.github/actions/windows-setup-env/action.yml b/.github/actions/windows-setup-env/action.yml index ece8e78806..515b71190d 100644 --- a/.github/actions/windows-setup-env/action.yml +++ b/.github/actions/windows-setup-env/action.yml @@ -7,6 +7,9 @@ inputs: java-version: description: "Java version to use in tests" default: "8" + llvm-version: + description: "LLVM version to use" + default: "11.0.0" outputs: vcpkg-dir: description: "Directory containing installed libraries" @@ -60,9 +63,10 @@ runs: key: ${{ runner.os }}-${{ inputs.scala-version }}-deps # Install LLVM in case if cache is missing + # Choco-Install is GH Actions wrappers around choco, which does retries - name: Install LLVM shell: pwsh - run: choco install llvm --version=11.0.0 --allow-downgrade + run: Choco-Install -PackageName llvm -ArgumentList "--version=${{ inputs.llvm-version }}", "--allow-downgrade", "--force" - name: Add LLVM on Path shell: pwsh diff --git a/.github/workflows/run-tests-macos.yml b/.github/workflows/run-tests-macos.yml index 35db8d626b..5b2c4993c7 100644 --- a/.github/workflows/run-tests-macos.yml +++ b/.github/workflows/run-tests-macos.yml @@ -7,11 +7,11 @@ on: - main - 0.4.x concurrency: - group: macOS-${{ github.head_ref }} + group: macOS-${{ github.head_ref }}-${{ github.event_name }} cancel-in-progress: true jobs: - run-tests: + test-runtime: name: Test runtime runs-on: macos-11 strategy: @@ -35,6 +35,7 @@ jobs: id: setup with: scala-version: ${{matrix.scala}} + gc: ${{ matrix.gc }} - name: Test runtime run: > diff --git a/.github/workflows/run-tests-windows.yml b/.github/workflows/run-tests-windows.yml index eb3feeaa57..179b3ea894 100644 --- a/.github/workflows/run-tests-windows.yml +++ b/.github/workflows/run-tests-windows.yml @@ -7,7 +7,7 @@ on: - main - 0.4.x concurrency: - group: windows-${{ github.head_ref }} + group: windows-${{ github.head_ref }}-${{ github.event_name }} cancel-in-progress: true jobs: @@ -132,3 +132,30 @@ jobs: tests${{env.project-version}}/test testsExt${{env.project-version}}/test shell: cmd + + test-llvm-versions: + runs-on: windows-2019 + strategy: + fail-fast: false + matrix: + scala: [3.2.2] + llvm: ["13.0.1", "14.0.6", "15.0.7"] # Last 3 stable versions + steps: + - name: Setup git config + run: git config --global core.autocrlf false + - uses: actions/checkout@v3 + - uses: ./.github/actions/windows-setup-env + id: setup + with: + scala-version: ${{matrix.scala}} + llvm-version: ${{ matrix.llvm }} + java-version: 8 + + - name: Run tests + shell: cmd + run: > + set SCALANATIVE_INCLUDE_DIRS=${{steps.setup.outputs.vcpkg-dir}}\include& + set SCALANATIVE_LIB_DIRS=${{steps.setup.outputs.vcpkg-dir}}\lib& + set SCALANATIVE_CI_NO_DEBUG_SYMBOLS=true& + set SCALANATIVE & + sbt ++${{matrix.scala}} "show tests3/nativeConfig" "test-runtime ${{matrix.scala}}" diff --git a/clib/src/main/resources/scala-native/math.c b/clib/src/main/resources/scala-native/math.c index f36640db91..223b9743ab 100644 --- a/clib/src/main/resources/scala-native/math.c +++ b/clib/src/main/resources/scala-native/math.c @@ -8,8 +8,14 @@ float scalanative_infinity() { return INFINITY; } float scalanative_nan() { return NAN; } +#if defined(math_errhandling) int scalanative_math_errhandling() { return math_errhandling; } +#endif +#if defined(MATH_ERRNO) int scalanative_math_errno() { return MATH_ERRNO; } +#endif +#if defined(MATH_ERREXCEPT) int scalanative_math_errexcept() { return MATH_ERREXCEPT; } +#endif diff --git a/clib/src/main/scala/scala/scalanative/libc/stdio.scala b/clib/src/main/scala/scala/scalanative/libc/stdio.scala index dbf94dd2af..658a49509a 100644 --- a/clib/src/main/scala/scala/scalanative/libc/stdio.scala +++ b/clib/src/main/scala/scala/scalanative/libc/stdio.scala @@ -2,6 +2,7 @@ package scala.scalanative package libc import scalanative.unsafe._ +import stddef.size_t @extern object stdio { @@ -116,6 +117,7 @@ object stdio { def fwide(stream: Ptr[FILE], mode: CInt): CInt = extern // Direct input/output + /** Reads up to count objects into the array buffer from the given input * stream stream as if by calling fgetc size times for each object, and * storing the results, in the order obtained, into the successive positions @@ -153,8 +155,7 @@ object stdio { size: CSize, count: CSize, stream: Ptr[FILE] - ): CSize = - extern + ): CSize = extern /** Writes count of objects from the given array buffer to the output stream * stream. The objects are written as if by reinterpreting each object as an @@ -412,6 +413,77 @@ object stdio { def ungetc(ch: CInt, stream: Ptr[FILE]): CInt = extern // Formatted input/output + + /** Reads data from stdin and stores them according to the parameter format + * into the locations pointed by the additional arguments. + * @param format + * C string that contains a sequence of characters that control how + * characters extracted from the stream are treated + * + * @param vargs + * Depending on the format string, the function may expect a sequence of + * additional arguments, each containing a pointer to allocated storage + * where the interpretation of the extracted characters is stored with the + * appropriate type. There should be at least as many of these arguments as + * the number of values stored by the format specifiers. Additional + * arguments are ignored by the function. + * @return + * the number of items of the argument listsuccessfully filled on success. + * If a reading error happens or the end-of-file is reached while reading, + * the proper indicator is set (feof or ferror). And, if either happens + * before any data could be successfully read, EOF is returned. + */ + def scanf(format: CString, vargs: Any*): CInt = extern + + /** Reads data from the stream and stores them according to the parameter + * format into the locations pointed by the additional arguments. + * @param stream + * Pointer to a FILE object that identifies the input stream to read data + * from. + * @param format + * C string that contains a sequence of characters that control how + * characters extracted from the stream are treated + * + * @param vargs + * Depending on the format string, the function may expect a sequence of + * additional arguments, each containing a pointer to allocated storage + * where the interpretation of the extracted characters is stored with the + * appropriate type. There should be at least as many of these arguments as + * the number of values stored by the format specifiers. Additional + * arguments are ignored by the function. + * @return + * the number of items of the argument listsuccessfully filled on success. + * If a reading error happens or the end-of-file is reached while reading, + * the proper indicator is set (feof or ferror). And, if either happens + * before any data could be successfully read, EOF is returned. + */ + def fscanf(stream: Ptr[FILE], format: CString, vargs: Any*): CInt = + extern + + /** Reads data from s and stores them according to parameter format into the + * locations given by the additional arguments, as if scanf was used, but + * reading from s instead of the standard input + * @param s + * C string that the function processes as its source to retrieve the data. + * @param format + * C string that contains a sequence of characters that control how + * characters extracted from the stream are treated + * + * @param vargs + * Depending on the format string, the function may expect a sequence of + * additional arguments, each containing a pointer to allocated storage + * where the interpretation of the extracted characters is stored with the + * appropriate type. There should be at least as many of these arguments as + * the number of values stored by the format specifiers. Additional + * arguments are ignored by the function. + * @return + * the number of items of the argument listsuccessfully filled on success. + * If a reading error happens or the end-of-file is reached while reading, + * the proper indicator is set (feof or ferror). And, if either happens + * before any data could be successfully read, EOF is returned. + */ + def sscanf(s: CString, format: CString, vargs: Any*): CInt = extern + /** Read formatted data into variable argument list Reads data from the * standard input (stdin) and stores them according to parameter format into * the locations pointed by the elements in the variable argument list @@ -472,6 +544,175 @@ object stdio { def vsscanf(buffer: CString, format: CString, valist: CVarArgList): CInt = extern + /** Writes the results to stdout. + * @param format + * pointer to a null-terminated character string specifying how to + * interpret the data + * @param vargs + * variable argument list containing the data to print. + * + * @return + * The number of characters written if successful or negative value if an + * error occurred. + * @see + * [[https://en.cppreference.com/w/c/io/printf]] + */ + def printf(format: CString, vargs: Any*): CInt = extern + + /** Writes the results to selected stream. + * @param stream + * output file stream to write to + * @param format + * pointer to a null-terminated character string specifying how to + * interpret the data + * @param vargs + * variable argument list containing the data to print. + * + * @return + * The number of characters written if successful or negative value if an + * error occurred. + * @see + * [[https://en.cppreference.com/w/c/io/fprintf]] + */ + def fprintf(stream: Ptr[FILE], format: CString, vargs: Any*): CInt = + extern + + /** Writes the results to a character string buffer. + * @param buffer + * pointer to a character string to write to + * @param format + * pointer to a null-terminated character string specifying how to + * interpret the data + * @param vargs + * variable argument list containing the data to print. + * + * @return + * The number of characters written if successful or negative value if an + * error occurred. + * @see + * [[https://en.cppreference.com/w/c/io/sprintf]] + */ + def sprintf( + buffer: Ptr[CChar], + format: CString, + vargs: Any* + ): CInt = extern + + /** Writes the results to a character string buffer. At most bufsz - 1 + * characters are written. The resulting character string will be terminated + * with a null character, unless bufsz is zero. If bufsz is zero, nothing is + * written and buffer may be a null pointer, however the return value (number + * of bytes that would be written not including the null terminator) is still + * calculated and returned. + * @param buffer + * pointer to a character string to write to + * @param busz + * number of character to write + * @param format + * pointer to a null-terminated character string specifying how to + * interpret the data + * @param vargs + * variable argument list containing the data to print. + * + * @return + * The number of characters written if successful or negative value if an + * error occurred. + * @see + * [[https://en.cppreference.com/w/c/io/snprintf]] + */ + def snprintf( + buffer: Ptr[CChar], + bufsz: size_t, + format: CString, + vargs: Any* + ): CInt = extern + + /** Writes the results to stdout. + * @param format + * pointer to a null-terminated character string specifying how to + * interpret the data + * @param vargs + * variable argument list containing the data to print. + * + * @return + * The number of characters written if successful or negative value if an + * error occurred. + * @see + * [[https://en.cppreference.com/w/c/io/printf_s]] + */ + def printf_s(format: CString, vargs: Any*): CInt = extern + + /** Writes the results to selected stream. + * @param stream + * output file stream to write to + * @param format + * pointer to a null-terminated character string specifying how to + * interpret the data + * @param vargs + * variable argument list containing the data to print. + * + * @return + * The number of characters written if successful or negative value if an + * error occurred. + * @see + * [[https://en.cppreference.com/w/c/io/fprintf_s]] + */ + def fprintf_s( + stream: Ptr[FILE], + format: CString, + vargs: Any* + ): CInt = extern + + /** Writes the results to a character string buffer. + * @param buffer + * pointer to a character string to write to + * @param format + * pointer to a null-terminated character string specifying how to + * interpret the data + * @param vargs + * variable argument list containing the data to print. + * + * @return + * The number of characters written if successful or negative value if an + * error occurred. + * @see + * [[https://en.cppreference.com/w/c/io/sprintf_s]] + */ + def sprintf_s( + buffer: Ptr[CChar], + format: CString, + vargs: Any* + ): CInt = extern + + /** Writes the results to a character string buffer. At most bufsz - 1 + * characters are written. The resulting character string will be terminated + * with a null character, unless bufsz is zero. If bufsz is zero, nothing is + * written and buffer may be a null pointer, however the return value (number + * of bytes that would be written not including the null terminator) is still + * calculated and returned. + * @param buffer + * pointer to a character string to write to + * @param busz + * number of character to write + * @param format + * pointer to a null-terminated character string specifying how to + * interpret the data + * @param vargs + * variable argument list containing the data to print. + * + * @return + * The number of characters written if successful or negative value if an + * error occurred. + * @see + * [[https://en.cppreference.com/w/c/io/snprintf_s]] + */ + def snprintf_s( + buffer: Ptr[CChar], + bufsz: size_t, + format: CString, + vargs: Any* + ): CInt = extern + /** Writes the results to stdout. * @param format * pointer to a null-terminated character string specifying how to diff --git a/docs/changelog/0.4.11.md b/docs/changelog/0.4.11.md new file mode 100644 index 0000000000..fbdd56b0d2 --- /dev/null +++ b/docs/changelog/0.4.11.md @@ -0,0 +1,178 @@ + +# 0.4.11 (2023-03-15) + +We're happy to announce the release of Scala Native. It's the next maintenance release for Scala Native 0.4.x. As always it brings bug fixes and minor improvements. + +## Notable changes + +### Extern methods with a variadic number of arguments +For a long time, Scala Native supported C `va_list` using `scalanative.unsafe.CVarArgList`. This allowed for interop with some of the C functions taking the variadic number of arguments. This release makes usage and definition of them easier, by restoring support for idiomatic ways of passing them using Scala variadic arguments lists. +```c +void printf(char* format, ...); +``` + +```scala +@extern def printf(format: CString, args: Any*): Unit = extern + +@main def test() = + val msg = c"MyMessage" + printf("String '%s' is allocated at %p and has %d characters\n", msg, msg, strlen(msg)) +``` + +### Support for LLVM 15 +The latest versions of LLVM added a new internal representation of pointers - their opaque variant replaces typed pointers. This change should not affect most of the users, but in some specific builds it could have lead to linking issues. +Now Scala Native will try to detect version of LLVM toolchain. When using LLVM 15 or newer Scala Native toolchain would always generate opaque pointers in the compiled LLVM IR. + +The Scala standard library used by this release is based on the following versions: +
Scala binary version | +Scala release | +
2.12 | +2.12.17 | +
2.13 | +2.13.10 | +
3 | +3.2.2 | +
Commits since last release | +43 | +
Merged PRs | +40 | +
Contributors | +6 | +
Scala binary version | +Scala release | +
2.12 | +2.12.17 | +
2.13 | +2.13.10 | +
3 | +3.2.2 | +
Commits since last release | +10 | +
Merged PRs | +8 | +
Contributors | +3 | +