diff --git a/.github/actions/build-jtreg/action.yml b/.github/actions/build-jtreg/action.yml index 0ba9937fb45..a9c046e9dd9 100644 --- a/.github/actions/build-jtreg/action.yml +++ b/.github/actions/build-jtreg/action.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/.github/actions/get-bundles/action.yml b/.github/actions/get-bundles/action.yml index 270d15159a0..a356aa9fd8d 100644 --- a/.github/actions/get-bundles/action.yml +++ b/.github/actions/get-bundles/action.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/.github/actions/get-gtest/action.yml b/.github/actions/get-gtest/action.yml index d38d33eabd8..7a329460a6e 100644 --- a/.github/actions/get-gtest/action.yml +++ b/.github/actions/get-gtest/action.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/.github/actions/get-jtreg/action.yml b/.github/actions/get-jtreg/action.yml index 4bb671d25d1..36c895fc59d 100644 --- a/.github/actions/get-jtreg/action.yml +++ b/.github/actions/get-jtreg/action.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/.github/actions/get-msys2/action.yml b/.github/actions/get-msys2/action.yml index d93b6e3763b..308230ebf2e 100644 --- a/.github/actions/get-msys2/action.yml +++ b/.github/actions/get-msys2/action.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/.github/actions/upload-bundles/action.yml b/.github/actions/upload-bundles/action.yml index ca5366f3d6c..78fb0a94bfd 100644 --- a/.github/actions/upload-bundles/action.yml +++ b/.github/actions/upload-bundles/action.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/.github/workflows/build-alpine-linux.yml b/.github/workflows/build-alpine-linux.yml index a39b342a248..c39962fa07f 100644 --- a/.github/workflows/build-alpine-linux.yml +++ b/.github/workflows/build-alpine-linux.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -96,6 +96,8 @@ jobs: --with-boot-jdk=${{ steps.bootjdk.outputs.path }} --with-zlib=system --with-jmod-compress=zip-1 + --with-external-symbols-in-bundles=none + --with-native-debug-symbols-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index e70937f57b6..a0642d469aa 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -179,6 +179,8 @@ jobs: --openjdk-target=${{ matrix.gnu-arch }}-linux-gnu${{ matrix.gnu-abi}} --with-sysroot=sysroot --with-jmod-compress=zip-1 + --with-external-symbols-in-bundles=none + --with-native-debug-symbols-level=1 CC=${{ matrix.gnu-arch }}-linux-gnu${{ matrix.gnu-abi}}-gcc-${{ inputs.gcc-major-version }} CXX=${{ matrix.gnu-arch }}-linux-gnu${{ matrix.gnu-abi}}-g++-${{ inputs.gcc-major-version }} ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 0680dea6bbe..791b53a3f04 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -143,6 +143,8 @@ jobs: --with-gtest=${{ steps.gtest.outputs.path }} --with-zlib=system --with-jmod-compress=zip-1 + --with-external-symbols-in-bundles=none + --with-native-debug-symbols-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 0a12df668e5..484e616fad7 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -110,6 +110,8 @@ jobs: --with-gtest=${{ steps.gtest.outputs.path }} --with-zlib=system --with-jmod-compress=zip-1 + --with-external-symbols-in-bundles=none + --with-native-debug-symbols-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index a3091b94cef..4dafc016a99 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -134,6 +134,7 @@ jobs: --with-gtest=${{ steps.gtest.outputs.path }} --with-msvc-toolset-version=${{ inputs.msvc-toolset-version }} --with-jmod-compress=zip-1 + --with-external-symbols-in-bundles=none ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 09e6ed65a47..85ec75f343c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f2c8916a369..8f33454305e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/.jcheck/conf b/.jcheck/conf index 60881e74d2a..25af49f8ef8 100644 --- a/.jcheck/conf +++ b/.jcheck/conf @@ -1,7 +1,7 @@ [general] project=jdk jbs=JDK -version=26 +version=27 [checks] error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists,copyright diff --git a/README.md b/README.md index b3f30676b3c..e939f6a9ca4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Welcome to the JDK! For build instructions please see the -[online documentation](https://openjdk.org/groups/build/doc/building.html), +[online documentation](https://git.openjdk.org/jdk/blob/master/doc/building.md), or either of these files: - [doc/building.html](doc/building.html) (html version) diff --git a/make/Bundles.gmk b/make/Bundles.gmk index cf3b77e4e52..0b324e7e3f3 100644 --- a/make/Bundles.gmk +++ b/make/Bundles.gmk @@ -125,13 +125,6 @@ define SetupBundleFileBody && $(TAR) cf - -$(TAR_INCLUDE_PARAM) $$($1_$$d_LIST_FILE) \ $(TAR_IGNORE_EXIT_VALUE) ) \ | ( $(CD) $(SUPPORT_OUTPUTDIR)/bundles/$1/$$($1_SUBDIR) && $(TAR) xf - )$$(NEWLINE) ) - # Rename stripped pdb files - ifeq ($(call isTargetOs, windows)+$(SHIP_DEBUG_SYMBOLS), true+public) - for f in `$(FIND) $(SUPPORT_OUTPUTDIR)/bundles/$1/$$($1_SUBDIR) -name "*.stripped.pdb"`; do \ - $(ECHO) Renaming $$$${f} to $$$${f%stripped.pdb}pdb $(LOG_INFO); \ - $(MV) $$$${f} $$$${f%stripped.pdb}pdb; \ - done - endif # Unzip any zipped debuginfo files ifeq ($$($1_UNZIP_DEBUGINFO), true) for f in `$(FIND) $(SUPPORT_OUTPUTDIR)/bundles/$1/$$($1_SUBDIR) -name "*.diz"`; do \ @@ -192,96 +185,30 @@ endif ifneq ($(filter product-bundles% legacy-bundles, $(MAKECMDGOALS)), ) - SYMBOLS_EXCLUDE_PATTERN := %.debuginfo %.diz %.map - - # There may be files with spaces in the names, so use ShellFindFiles - # explicitly. + # There may be files with spaces in the names, so use ShellFindFiles explicitly. ALL_JDK_FILES := $(call ShellFindFiles, $(JDK_IMAGE_DIR)) - ifneq ($(JDK_IMAGE_DIR), $(JDK_SYMBOLS_IMAGE_DIR)) - ALL_JDK_SYMBOLS_FILES := $(call ShellFindFiles, $(JDK_SYMBOLS_IMAGE_DIR)) - else - ALL_JDK_SYMBOLS_FILES := $(ALL_JDK_FILES) - endif ifneq ($(JDK_IMAGE_DIR), $(JDK_DEMOS_IMAGE_DIR)) ALL_JDK_DEMOS_FILES := $(call ShellFindFiles, $(JDK_DEMOS_IMAGE_DIR)) else ALL_JDK_DEMOS_FILES := $(ALL_JDK_FILES) endif - # Create special filter rules when dealing with unzipped .dSYM directories on - # macosx - ifeq ($(call isTargetOs, macosx), true) - ifeq ($(ZIP_EXTERNAL_DEBUG_SYMBOLS), false) - JDK_SYMBOLS_EXCLUDE_PATTERN := $(addprefix %, \ - $(call containing, .dSYM/, $(patsubst $(JDK_IMAGE_DIR)/%, %, \ - $(ALL_JDK_SYMBOLS_FILES)))) - endif - endif - - # Create special filter rules when dealing with debug symbols on windows - ifeq ($(call isTargetOs, windows), true) - ifeq ($(SHIP_DEBUG_SYMBOLS), ) - JDK_SYMBOLS_EXCLUDE_PATTERN := %.pdb - else - ifeq ($(SHIP_DEBUG_SYMBOLS), public) - JDK_SYMBOLS_EXCLUDE_PATTERN := \ - $(filter-out \ - %.stripped.pdb, \ - $(filter %.pdb, $(ALL_JDK_FILES)) \ - ) - endif - endif - endif - JDK_BUNDLE_FILES := \ $(filter-out \ - $(JDK_SYMBOLS_EXCLUDE_PATTERN) \ $(JDK_EXTRA_EXCLUDES) \ - $(SYMBOLS_EXCLUDE_PATTERN) \ $(JDK_IMAGE_HOMEDIR)/demo/% \ , \ $(ALL_JDK_FILES) \ ) - JDK_SYMBOLS_BUNDLE_FILES := \ - $(filter-out \ - %.stripped.pdb, \ - $(call FindFiles, $(SYMBOLS_IMAGE_DIR)) \ - ) + JDK_SYMBOLS_BUNDLE_FILES := $(call FindFiles, $(SYMBOLS_IMAGE_DIR)) TEST_DEMOS_BUNDLE_FILES := $(filter $(JDK_DEMOS_IMAGE_HOMEDIR)/demo/%, \ $(ALL_JDK_DEMOS_FILES)) ALL_JRE_FILES := $(call ShellFindFiles, $(JRE_IMAGE_DIR)) - # Create special filter rules when dealing with unzipped .dSYM directories on - # macosx - ifeq ($(OPENJDK_TARGET_OS), macosx) - ifeq ($(ZIP_EXTERNAL_DEBUG_SYMBOLS), false) - JRE_SYMBOLS_EXCLUDE_PATTERN := $(addprefix %, \ - $(call containing, .dSYM/, $(patsubst $(JRE_IMAGE_DIR)/%, %, $(ALL_JRE_FILES)))) - endif - endif - - # Create special filter rules when dealing with debug symbols on windows - ifeq ($(call isTargetOs, windows), true) - ifeq ($(SHIP_DEBUG_SYMBOLS), ) - JRE_SYMBOLS_EXCLUDE_PATTERN := %.pdb - else - ifeq ($(SHIP_DEBUG_SYMBOLS), public) - JRE_SYMBOLS_EXCLUDE_PATTERN := \ - $(filter-out \ - %.stripped.pdb, \ - $(filter %.pdb, $(ALL_JRE_FILES)) \ - ) - endif - endif - endif - - JRE_BUNDLE_FILES := $(filter-out \ - $(JRE_SYMBOLS_EXCLUDE_PATTERN) \ - $(SYMBOLS_EXCLUDE_PATTERN), \ - $(ALL_JRE_FILES)) + JRE_BUNDLE_FILES := $(ALL_JRE_FILES) ifeq ($(MACOSX_CODESIGN_MODE), hardened) # Macosx release build and code signing available. diff --git a/make/CreateJmods.gmk b/make/CreateJmods.gmk index 3280b24924a..32f107b6bb6 100644 --- a/make/CreateJmods.gmk +++ b/make/CreateJmods.gmk @@ -218,10 +218,14 @@ ifeq ($(call isTargetOs, windows), true) ifeq ($(SHIP_DEBUG_SYMBOLS), ) JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.pdb,*.map}' else - JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.map}' + JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.map}' endif else - JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.debuginfo,*.dSYM/**,*.dSYM}' + ifeq ($(SHIP_DEBUG_SYMBOLS), ) + JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.debuginfo,*.dSYM/**,*.dSYM}' + else + JMOD_FLAGS += --exclude '**{_the.*,_*.marker*}' + endif endif # Unless we are creating a very large module, use the small tool JVM options diff --git a/make/Docs.gmk b/make/Docs.gmk index b105e1d8683..a8d40e078e2 100644 --- a/make/Docs.gmk +++ b/make/Docs.gmk @@ -93,16 +93,19 @@ JAVADOC_DISABLED_DOCLINT_WARNINGS := missing JAVADOC_DISABLED_DOCLINT_PACKAGES := org.w3c.* javax.smartcardio # The initial set of options for javadoc +# -XDaccessInternalAPI is a temporary workaround, see 8373909 JAVADOC_OPTIONS := -use -keywords -notimestamp \ -serialwarn -encoding utf-8 -docencoding utf-8 -breakiterator \ -splitIndex --system none -javafx --expand-requires transitive \ - --override-methods=summary + --override-methods=summary \ + -XDaccessInternalAPI # The reference options must stay stable to allow for comparisons across the # development cycle. REFERENCE_OPTIONS := -XDignore.symbol.file=true -use -keywords -notimestamp \ -serialwarn -encoding utf-8 -breakiterator -splitIndex --system none \ - -html5 -javafx --expand-requires transitive + -html5 -javafx --expand-requires transitive \ + -XDaccessInternalAPI # Should we add DRAFT stamps to the generated javadoc? ifeq ($(VERSION_IS_GA), true) diff --git a/make/Images.gmk b/make/Images.gmk index c5877e44c22..89c0a834477 100644 --- a/make/Images.gmk +++ b/make/Images.gmk @@ -282,29 +282,33 @@ else endif CMDS_TARGET_SUBDIR := bin -# Param 1 - either JDK or JRE +# Copy debug info files into symbols bundle. +# In case of Windows and --with-external-symbols-in-bundles=public, take care to remove *.stripped.pdb files SetupCopyDebuginfo = \ $(foreach m, $(ALL_$1_MODULES), \ + $(eval dbgfiles := $(call FindDebuginfoFiles, $(SUPPORT_OUTPUTDIR)/modules_libs/$m)) \ + $(eval dbgfiles := $(if $(filter true+public,$(call isTargetOs,windows)+$(SHIP_DEBUG_SYMBOLS)), \ + $(filter-out %.stripped.pdb,$(dbgfiles)),$(dbgfiles)) \ + ) \ $(eval $(call SetupCopyFiles, COPY_$1_LIBS_DEBUGINFO_$m, \ SRC := $(SUPPORT_OUTPUTDIR)/modules_libs/$m, \ DEST := $($1_IMAGE_DIR)/$(LIBS_TARGET_SUBDIR), \ - FILES := $(call FindDebuginfoFiles, \ - $(SUPPORT_OUTPUTDIR)/modules_libs/$m), \ + FILES := $(dbgfiles), \ )) \ $(eval $1_TARGETS += $$(COPY_$1_LIBS_DEBUGINFO_$m)) \ + $(eval dbgfiles := $(call FindDebuginfoFiles, $(SUPPORT_OUTPUTDIR)/modules_cmds/$m)) \ + $(eval dbgfiles := $(if $(filter true+public,$(call isTargetOs,windows)+$(SHIP_DEBUG_SYMBOLS)), \ + $(filter-out %.stripped.pdb,$(dbgfiles)),$(dbgfiles)) \ + ) \ $(eval $(call SetupCopyFiles, COPY_$1_CMDS_DEBUGINFO_$m, \ SRC := $(SUPPORT_OUTPUTDIR)/modules_cmds/$m, \ DEST := $($1_IMAGE_DIR)/$(CMDS_TARGET_SUBDIR), \ - FILES := $(call FindDebuginfoFiles, \ - $(SUPPORT_OUTPUTDIR)/modules_cmds/$m), \ + FILES := $(dbgfiles), \ )) \ $(eval $1_TARGETS += $$(COPY_$1_CMDS_DEBUGINFO_$m)) \ ) -# No space before argument to avoid having to put $(strip ) everywhere in -# implementation above. -$(call SetupCopyDebuginfo,JDK) -$(call SetupCopyDebuginfo,JRE) +# No space before argument to avoid having to put $(strip ) everywhere in implementation above. $(call SetupCopyDebuginfo,SYMBOLS) ################################################################################ diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 1f50b97531b..946b1332edc 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -873,7 +873,7 @@ define SetupRunJtregTestBody $1_JTREG_BASIC_OPTIONS += -testThreadFactoryPath:$$(JTREG_TEST_THREAD_FACTORY_JAR) $1_JTREG_BASIC_OPTIONS += -testThreadFactory:$$(JTREG_TEST_THREAD_FACTORY) $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \ - $$(addprefix $$($1_TEST_ROOT)/, ProblemList-$$(JTREG_TEST_THREAD_FACTORY).txt) \ + $$(addprefix $$($1_TEST_ROOT)/, ProblemList-$$(JTREG_TEST_THREAD_FACTORY).txt) \ )) endif @@ -881,8 +881,8 @@ define SetupRunJtregTestBody AGENT := $$(LIBRARY_PREFIX)JvmtiStressAgent$$(SHARED_LIBRARY_SUFFIX)=$$(JTREG_JVMTI_STRESS_AGENT) $1_JTREG_BASIC_OPTIONS += -javaoption:'-agentpath:$(TEST_IMAGE_DIR)/hotspot/jtreg/native/$$(AGENT)' $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \ - $$(addprefix $$($1_TEST_ROOT)/, ProblemList-jvmti-stress-agent.txt) \ - )) + $$(addprefix $$($1_TEST_ROOT)/, ProblemList-jvmti-stress-agent.txt) \ + )) endif @@ -1092,7 +1092,7 @@ define SetupRunJtregTestBody $$(call MakeDir, $$($1_TEST_RESULTS_DIR) $$($1_TEST_SUPPORT_DIR) \ $$($1_TEST_TMP_DIR)) $$(call ExecuteWithLog, $$($1_TEST_SUPPORT_DIR)/jtreg, \ - $$(COV_ENVIRONMENT) $$($1_COMMAND_LINE) \ + $$(COV_ENVIRONMENT) $$($1_COMMAND_LINE) \ ) $1_RESULT_FILE := $$($1_TEST_RESULTS_DIR)/text/stats.txt @@ -1102,11 +1102,11 @@ define SetupRunJtregTestBody $$(call LogWarn, Test report is stored in $$(strip \ $$(subst $$(TOPDIR)/, , $$($1_TEST_RESULTS_DIR)))) - # Read jtreg documentation to learn on the test stats categories: - # https://github.com/openjdk/jtreg/blob/master/src/share/doc/javatest/regtest/faq.md#what-do-all-those-numbers-in-the-test-results-line-mean - # In jtreg, "skipped:" category accounts for tests that threw jtreg.SkippedException at runtime. - # At the same time these tests contribute to "passed:" tests. - # In here we don't want that and so we substract number of "skipped:" from "passed:". + # Read jtreg documentation to learn on the test stats categories: + # https://github.com/openjdk/jtreg/blob/master/src/share/doc/javatest/regtest/faq.md#what-do-all-those-numbers-in-the-test-results-line-mean + # In jtreg, "skipped:" category accounts for tests that threw jtreg.SkippedException at runtime. + # At the same time these tests contribute to "passed:" tests. + # In here we don't want that and so we substract number of "skipped:" from "passed:". $$(if $$(wildcard $$($1_RESULT_FILE)), \ $$(eval $1_PASSED_AND_RUNTIME_SKIPPED := $$(shell $$(AWK) '{ gsub(/[,;]/, ""); \ diff --git a/make/RunTestsPrebuiltSpec.gmk b/make/RunTestsPrebuiltSpec.gmk index e9fd901c9e1..5fe559eafad 100644 --- a/make/RunTestsPrebuiltSpec.gmk +++ b/make/RunTestsPrebuiltSpec.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/autoconf/bootcycle-spec.gmk.template b/make/autoconf/bootcycle-spec.gmk.template index d17a95e190a..eb564f40e3f 100644 --- a/make/autoconf/bootcycle-spec.gmk.template +++ b/make/autoconf/bootcycle-spec.gmk.template @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/autoconf/buildjdk-spec.gmk.template b/make/autoconf/buildjdk-spec.gmk.template index 924389b94e8..bb020842d59 100644 --- a/make/autoconf/buildjdk-spec.gmk.template +++ b/make/autoconf/buildjdk-spec.gmk.template @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/autoconf/compare.sh.template b/make/autoconf/compare.sh.template index 84421035ab9..b17ddc16827 100644 --- a/make/autoconf/compare.sh.template +++ b/make/autoconf/compare.sh.template @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index 6298bcae416..5a9fdc57c74 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -69,6 +69,23 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], # Debug prefix mapping if supported by compiler DEBUG_PREFIX_CFLAGS= + UTIL_ARG_WITH(NAME: native-debug-symbols-level, TYPE: string, + DEFAULT: "", + RESULT: DEBUG_SYMBOLS_LEVEL, + DESC: [set the native debug symbol level (GCC and Clang only)], + DEFAULT_DESC: [toolchain default]) + AC_SUBST(DEBUG_SYMBOLS_LEVEL) + + if test "x${TOOLCHAIN_TYPE}" = xgcc || \ + test "x${TOOLCHAIN_TYPE}" = xclang; then + DEBUG_SYMBOLS_LEVEL_FLAGS="-g" + if test "x${DEBUG_SYMBOLS_LEVEL}" != "x"; then + DEBUG_SYMBOLS_LEVEL_FLAGS="-g${DEBUG_SYMBOLS_LEVEL}" + FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${DEBUG_SYMBOLS_LEVEL_FLAGS}], + IF_FALSE: AC_MSG_ERROR("Debug info level ${DEBUG_SYMBOLS_LEVEL} is not supported")) + fi + fi + # Debug symbols if test "x$TOOLCHAIN_TYPE" = xgcc; then if test "x$ALLOW_ABSOLUTE_PATHS_IN_OUTPUT" = "xfalse"; then @@ -93,8 +110,9 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], ) fi - CFLAGS_DEBUG_SYMBOLS="-g -gdwarf-4" - ASFLAGS_DEBUG_SYMBOLS="-g" + # Debug info level should follow the debug format to be effective. + CFLAGS_DEBUG_SYMBOLS="-gdwarf-4 ${DEBUG_SYMBOLS_LEVEL_FLAGS}" + ASFLAGS_DEBUG_SYMBOLS="${DEBUG_SYMBOLS_LEVEL_FLAGS}" elif test "x$TOOLCHAIN_TYPE" = xclang; then if test "x$ALLOW_ABSOLUTE_PATHS_IN_OUTPUT" = "xfalse"; then # Check if compiler supports -fdebug-prefix-map. If so, use that to make @@ -113,8 +131,9 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${GDWARF_FLAGS}], IF_FALSE: [GDWARF_FLAGS=""]) - CFLAGS_DEBUG_SYMBOLS="-g ${GDWARF_FLAGS}" - ASFLAGS_DEBUG_SYMBOLS="-g" + # Debug info level should follow the debug format to be effective. + CFLAGS_DEBUG_SYMBOLS="${GDWARF_FLAGS} ${DEBUG_SYMBOLS_LEVEL_FLAGS}" + ASFLAGS_DEBUG_SYMBOLS="${DEBUG_SYMBOLS_LEVEL_FLAGS}" elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then CFLAGS_DEBUG_SYMBOLS="-Z7" fi diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4 index b0dc565b39f..466ff1beaf4 100644 --- a/make/autoconf/flags-ldflags.m4 +++ b/make/autoconf/flags-ldflags.m4 @@ -63,7 +63,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], fi BASIC_LDFLAGS_JVM_ONLY="" - LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing" + LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing $DEBUG_PREFIX_CFLAGS" LDFLAGS_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r" @@ -71,7 +71,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], BASIC_LDFLAGS_JVM_ONLY="-mno-omit-leaf-frame-pointer -mstack-alignment=16 \ -fPIC" - LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing" + LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing $DEBUG_PREFIX_CFLAGS" LDFLAGS_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r" if test "x$OPENJDK_TARGET_OS" = xlinux; then diff --git a/make/autoconf/hotspot.m4 b/make/autoconf/hotspot.m4 index 6dc46d17aa3..3adb1333fe5 100644 --- a/make/autoconf/hotspot.m4 +++ b/make/autoconf/hotspot.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index bb188778001..87d147d4f07 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -316,23 +316,36 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_SYMBOLS], AC_MSG_CHECKING([if we should add external native debug symbols to the shipped bundles]) AC_ARG_WITH([external-symbols-in-bundles], [AS_HELP_STRING([--with-external-symbols-in-bundles], - [which type of external native debug symbol information shall be shipped in product bundles (none, public, full) - (e.g. ship full/stripped pdbs on Windows) @<:@none@:>@])]) + [which type of external native debug symbol information shall be shipped with bundles/images (none, public, full). + @<:@none in release builds, full otherwise. --with-native-debug-symbols=external/zipped is a prerequisite. public is only supported on Windows@:>@])], + [], + [with_external_symbols_in_bundles=default]) if test "x$with_external_symbols_in_bundles" = x || test "x$with_external_symbols_in_bundles" = xnone ; then AC_MSG_RESULT([no]) elif test "x$with_external_symbols_in_bundles" = xfull || test "x$with_external_symbols_in_bundles" = xpublic ; then - if test "x$OPENJDK_TARGET_OS" != xwindows ; then - AC_MSG_ERROR([--with-external-symbols-in-bundles currently only works on windows!]) - elif test "x$COPY_DEBUG_SYMBOLS" != xtrue ; then - AC_MSG_ERROR([--with-external-symbols-in-bundles only works when --with-native-debug-symbols=external is used!]) - elif test "x$with_external_symbols_in_bundles" = xfull ; then + if test "x$COPY_DEBUG_SYMBOLS" != xtrue ; then + AC_MSG_ERROR([--with-external-symbols-in-bundles only works when --with-native-debug-symbols=external/zipped is used!]) + elif test "x$with_external_symbols_in_bundles" = xpublic && test "x$OPENJDK_TARGET_OS" != xwindows ; then + AC_MSG_ERROR([--with-external-symbols-in-bundles=public is only supported on Windows!]) + fi + + if test "x$with_external_symbols_in_bundles" = xfull ; then AC_MSG_RESULT([full]) SHIP_DEBUG_SYMBOLS=full else AC_MSG_RESULT([public]) SHIP_DEBUG_SYMBOLS=public fi + elif test "x$with_external_symbols_in_bundles" = xdefault ; then + if test "x$DEBUG_LEVEL" = xrelease ; then + AC_MSG_RESULT([no (default)]) + elif test "x$COPY_DEBUG_SYMBOLS" = xtrue ; then + AC_MSG_RESULT([full (default)]) + SHIP_DEBUG_SYMBOLS=full + else + AC_MSG_RESULT([no (default, native debug symbols are not external/zipped)]) + fi else AC_MSG_ERROR([$with_external_symbols_in_bundles is an unknown value for --with-external-symbols-in-bundles]) fi diff --git a/make/autoconf/lib-bundled.m4 b/make/autoconf/lib-bundled.m4 index 3246697663c..a6266bec014 100644 --- a/make/autoconf/lib-bundled.m4 +++ b/make/autoconf/lib-bundled.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/autoconf/platform.m4 b/make/autoconf/platform.m4 index 31451d0c37f..1b247e159ac 100644 --- a/make/autoconf/platform.m4 +++ b/make/autoconf/platform.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/common/Utils.gmk b/make/common/Utils.gmk index c0ebabca3f7..b4bb949d3ee 100644 --- a/make/common/Utils.gmk +++ b/make/common/Utils.gmk @@ -114,7 +114,7 @@ EscapeDollar = $(subst $$,\$$,$(subst \$$,$$,$(strip $1))) ################################################################################ # This macro works just like EscapeDollar above, but for #. -EscapeHash = $(subst \#,\\\#,$(subst \\\#,\#,$(strip $1))) +EscapeHash = $(subst $(HASH),\$(HASH),$(subst \$(HASH),$(HASH),$(strip $1))) ################################################################################ # This macro translates $ into $$ to protect the string from make itself. diff --git a/make/common/native/Flags.gmk b/make/common/native/Flags.gmk index efb4c08e74c..6353b490654 100644 --- a/make/common/native/Flags.gmk +++ b/make/common/native/Flags.gmk @@ -234,6 +234,9 @@ define SetupLinkerFlags ifeq ($(call isTargetOs, macosx), true) $1_EXTRA_LDFLAGS += -Wl,-object_path_lto,$$($1_OBJECT_DIR)/$$($1_NAME)_lto_helper.o endif + ifeq ($(TOOLCHAIN_TYPE), microsoft) + $1_EXTRA_LDFLAGS += -LTCGOUT:$$($1_OBJECT_DIR)/$$($1_NAME).iobj + endif endif $1_EXTRA_LDFLAGS += $$($1_LDFLAGS_$(OPENJDK_TARGET_OS_TYPE)) $$($1_LDFLAGS_$(OPENJDK_TARGET_OS)) \ diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 795335d7c3c..93aeebc0dd6 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -1192,8 +1192,8 @@ var getJibProfilesDependencies = function (input, common) { server: "jpg", product: "jcov", version: "3.0", - build_number: "3", - file: "bundles/jcov-3.0+3.zip", + build_number: "5", + file: "bundles/jcov-3.0+5.zip", environment_name: "JCOV_HOME", }, diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index 977809535ba..4392d86ac33 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -26,17 +26,17 @@ # Default version, product, and vendor information to use, # unless overridden by configure -DEFAULT_VERSION_FEATURE=26 +DEFAULT_VERSION_FEATURE=27 DEFAULT_VERSION_INTERIM=0 DEFAULT_VERSION_UPDATE=0 DEFAULT_VERSION_PATCH=0 DEFAULT_VERSION_EXTRA1=0 DEFAULT_VERSION_EXTRA2=0 DEFAULT_VERSION_EXTRA3=0 -DEFAULT_VERSION_DATE=2026-03-17 -DEFAULT_VERSION_CLASSFILE_MAJOR=70 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" +DEFAULT_VERSION_DATE=2026-09-15 +DEFAULT_VERSION_CLASSFILE_MAJOR=71 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 -DEFAULT_ACCEPTABLE_BOOT_VERSIONS="25 26" -DEFAULT_JDK_SOURCE_TARGET_VERSION=26 +DEFAULT_ACCEPTABLE_BOOT_VERSIONS="25 26 27" +DEFAULT_JDK_SOURCE_TARGET_VERSION=27 DEFAULT_PROMOTED_VERSION_PRE=ea diff --git a/make/devkit/createWindowsDevkit.sh b/make/devkit/createWindowsDevkit.sh index 7c55605c776..b21557ed498 100644 --- a/make/devkit/createWindowsDevkit.sh +++ b/make/devkit/createWindowsDevkit.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk index 60912992134..327014b1e9d 100644 --- a/make/hotspot/lib/CompileGtest.gmk +++ b/make/hotspot/lib/CompileGtest.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -61,7 +61,8 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBGTEST, \ INCLUDE_FILES := gtest-all.cc gmock-all.cc, \ DISABLED_WARNINGS_gcc := format-nonliteral maybe-uninitialized undef \ unused-result zero-as-null-pointer-constant, \ - DISABLED_WARNINGS_clang := format-nonliteral undef unused-result, \ + DISABLED_WARNINGS_clang := format-nonliteral undef unused-result \ + zero-as-null-pointer-constant, \ DISABLED_WARNINGS_microsoft := 4530, \ DEFAULT_CFLAGS := false, \ CFLAGS := $(JVM_CFLAGS) \ diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk index b0ea27e5081..39a549b7db0 100644 --- a/make/hotspot/lib/CompileJvm.gmk +++ b/make/hotspot/lib/CompileJvm.gmk @@ -151,6 +151,12 @@ JVM_STRIPFLAGS ?= $(STRIPFLAGS) # This source set is reused so save in cache. $(call FillFindCache, $(JVM_SRC_DIRS)) +ifeq ($(SHIP_DEBUG_SYMBOLS), full) + CFLAGS_SHIP_DEBUGINFO := -DSHIP_DEBUGINFO_FULL +else ifeq ($(SHIP_DEBUG_SYMBOLS), public) + CFLAGS_SHIP_DEBUGINFO := -DSHIP_DEBUGINFO_PUBLIC +endif + ifeq ($(call isTargetOs, windows), true) ifeq ($(STATIC_LIBS), true) WIN_EXPORT_FILE := $(JVM_OUTPUTDIR)/static-win-exports.def @@ -158,10 +164,6 @@ ifeq ($(call isTargetOs, windows), true) WIN_EXPORT_FILE := $(JVM_OUTPUTDIR)/win-exports.def endif - ifeq ($(SHIP_DEBUG_SYMBOLS), public) - CFLAGS_STRIPPED_DEBUGINFO := -DHAS_STRIPPED_DEBUGINFO - endif - JVM_LDFLAGS += -def:$(WIN_EXPORT_FILE) endif @@ -187,7 +189,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \ CFLAGS := $(JVM_CFLAGS), \ abstract_vm_version.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \ arguments.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \ - whitebox.cpp_CXXFLAGS := $(CFLAGS_STRIPPED_DEBUGINFO), \ + whitebox.cpp_CXXFLAGS := $(CFLAGS_SHIP_DEBUGINFO), \ DISABLED_WARNINGS_gcc := $(DISABLED_WARNINGS_gcc), \ DISABLED_WARNINGS_gcc_ad_$(HOTSPOT_TARGET_CPU_ARCH).cpp := nonnull, \ DISABLED_WARNINGS_gcc_bytecodeInterpreter.cpp := unused-label, \ diff --git a/make/jdk/src/classes/build/tools/pandocfilter/PandocFilter.java b/make/jdk/src/classes/build/tools/pandocfilter/PandocFilter.java index ebb49613e53..9f4d84ac95b 100644 --- a/make/jdk/src/classes/build/tools/pandocfilter/PandocFilter.java +++ b/make/jdk/src/classes/build/tools/pandocfilter/PandocFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/jdk/src/classes/build/tools/taglet/JSpec.java b/make/jdk/src/classes/build/tools/taglet/JSpec.java index ca45104e086..1c301e94a8a 100644 --- a/make/jdk/src/classes/build/tools/taglet/JSpec.java +++ b/make/jdk/src/classes/build/tools/taglet/JSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,10 +31,9 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.lang.reflect.Field; import javax.lang.model.element.Element; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; import com.sun.source.doctree.DocTree; import com.sun.source.doctree.LiteralTree; @@ -160,9 +159,10 @@ public class JSpec implements Taglet { if (m.find()) { String chapter = m.group("chapter"); String section = m.group("section"); + String rootParent = currentPath().replaceAll("[^/]+", ".."); - String url = String.format("%1$s/../specs/%2$s/%2$s-%3$s.html#%2$s-%3$s%4$s", - docRoot(elem), idPrefix, chapter, section); + String url = String.format("%1$s/specs/%2$s/%2$s-%3$s.html#%2$s-%3$s%4$s", + rootParent, idPrefix, chapter, section); sb.append(" CURRENT_PATH = null; + + private String currentPath() { + if (CURRENT_PATH == null) { + try { + Field f = Class.forName("jdk.javadoc.internal.doclets.formats.html.HtmlDocletWriter") + .getField("CURRENT_PATH"); + @SuppressWarnings("unchecked") + ThreadLocal tl = (ThreadLocal) f.get(null); + CURRENT_PATH = tl; + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Cannot determine current path", e); + } + } + return CURRENT_PATH.get(); + } private String expand(List trees) { return (new SimpleDocTreeVisitor() { @@ -209,34 +225,4 @@ public class JSpec implements Taglet { }).visit(trees, new StringBuilder()).toString(); } - private String docRoot(Element elem) { - switch (elem.getKind()) { - case MODULE: - return ".."; - - case PACKAGE: - PackageElement pe = (PackageElement)elem; - String pkgPart = pe.getQualifiedName() - .toString() - .replace('.', '/') - .replaceAll("[^/]+", ".."); - return pe.getEnclosingElement() != null - ? "../" + pkgPart - : pkgPart; - - case CLASS, ENUM, RECORD, INTERFACE, ANNOTATION_TYPE: - TypeElement te = (TypeElement)elem; - return te.getQualifiedName() - .toString() - .replace('.', '/') - .replaceAll("[^/]+", ".."); - - default: - var enclosing = elem.getEnclosingElement(); - if (enclosing == null) - throw new IllegalArgumentException(elem.getKind().toString()); - return docRoot(enclosing); - } - } - } diff --git a/make/jdk/src/classes/build/tools/taglet/ToolGuide.java b/make/jdk/src/classes/build/tools/taglet/ToolGuide.java index 72a8ab05b3b..8db2aee3092 100644 --- a/make/jdk/src/classes/build/tools/taglet/ToolGuide.java +++ b/make/jdk/src/classes/build/tools/taglet/ToolGuide.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,10 +31,9 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.lang.reflect.Field; import javax.lang.model.element.Element; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; import com.sun.source.doctree.DocTree; import com.sun.source.doctree.UnknownBlockTagTree; @@ -68,7 +67,7 @@ public class ToolGuide implements Taglet { static final String TAG_NAME = "toolGuide"; - static final String BASE_URL = "../specs/man"; + static final String BASE_URL = "specs/man"; static final Pattern TAG_PATTERN = Pattern.compile("(?s)(?[A-Za-z0-9]+)\\s*(?
\n ランチャにコマンド・ライン引数が指定されていない場合にメイン・クラスに渡す\n コマンド・ライン引数\n このオプションは複数回使用できます。\n --java-options \n Javaランタイムに渡すオプション\n このオプションは複数回使用できます。\n --main-class \n 実行するアプリケーション・メイン・クラスの修飾名\n このオプションを使用できるのは、--main-jarが指定されている場合だけです。\n --main-jar
\n メイン・クラスを含む、アプリケーションのメインJAR\n (入力パスからの相対パスとして指定)\n --moduleまたは--main-jarオプションを指定できますが、両方は\n 指定できません。\n --module -m [/
]\n アプリケーションのメイン・モジュール(およびオプションでメイン・クラス)\n このモジュールは、モジュール・パスに置かれている必要があります。\n このオプションが指定されている場合、メイン・モジュールは\n Javaランタイム・イメージ内でリンクされます。--moduleまたは--main-jar\n オプションを指定できますが、両方は指定できません。\n{2}\nアプリケーション・パッケージを作成するためのオプション:\n --about-url \n アプリケーションのホームページのURL\n --app-image \n {5} (絶対パスまたは現在のディレクトリからの相対パス)\n --file-associations \n キー、値のペアのリストを含むプロパティ・ファイルへのパス\n \ -(絶対パスまたは現在のディレクトリからの相対パス)\n キー"extension"、"mime-type"、"icon"、"description"\n を使用して関連付けを記述できます。\n このオプションは複数回使用できます。\n --install-dir \n {4} --license-file \n ライセンス・ファイルへのパス\n (絶対パスまたは現在のディレクトリからの相対パス)\n --resource-dir \n オーバーライドjpackageリソースへのパス\n アイコン、テンプレート・ファイルおよびjpackageのその他のリソースは、\n このディレクトリに置換リソースを追加することでオーバーライドできます。\n (絶対パスまたは現在のディレクトリからの相対パス)\n --runtime-image \n インストールする事前定義済みのランタイム・イメージのパス\n (絶対パスまたは現在のディレクトリからの相対パス)\n ランタイム・パッケージの作成時には、オプションが必要です。\n --launcher-as-service\n 次として登録するインストーラの作成をリクエストします: \n バックグラウンド・サービス・タイプ・アプリケーションとしてのメイン・アプリケーション・ランチャ。\n\nアプリケーション・パッケージを作成するためのプラットフォーム依存オプション:\n{3} +help.header=使用方法: jpackage + +help.short=利用可能なオプションのリストについては、jpackage --help (or -h)を使用します + +help.option-group.sample.create-native-package=\ ホスト・システムに適したアプリケーション・パッケージを生成します:\n モジュラ・アプリケーションの場合:\n jpackage -n name -p modulePath -m moduleName/className\n 非モジュラ・アプリケーションの場合:\n jpackage -i inputDir -n name \\\n --main-class className --main-jar myJar.jar\n 事前作成されたアプリケーション・イメージから:\n jpackage -n name --app-image appImageDir + +help.option-group.sample.create-app-image=\ アプリケーション・イメージの生成:\n モジュラ・アプリケーションの場合:\n jpackage --type app-image -n name -p modulePath \\\n -m moduleName/className\n 非モジュラ・アプリケーションの場合:\n jpackage --type app-image -i inputDir -n name \\\n --main-class className --main-jar myJar.jar\n jlinkに独自のオプションを指定するには、jlinkを別個に実行します:\n jlink --output appRuntimeImage -p modulePath \\\n --add-modules moduleName \\\n --no-header-files [...]\n jpackage --type app-image -n name \\\n -m moduleName/className --runtime-image appRuntimeImage + +help.option-group.sample.create-runtime-installer=\ Javaランタイム・パッケージを生成します:\n jpackage -n name --runtime-image + +help.option-group.sample.sign-app-image=\ 事前定義済みアプリケーション・イメージへの署名:\n jpackage --type app-image --app-image \\\n --mac-sign [...]\n ノート: このモードで許可される唯一の追加オプション:\n 追加のmac署名オプションのセットおよび--verbose + +help.option-group.sample=使用例 +help.option-group.generic=一般的なオプション +help.option-group.runtime-image=ランタイム・イメージを作成するためのオプション +help.option-group.app-image=アプリケーション・イメージを作成するためのオプション +help.option-group.launcher=アプリケーション・ランチャを作成するためのオプション +help.option-group.launcher-platform=アプリケーション・ランチャを作成するためのプラットフォーム依存オプション +help.option-group.package=アプリケーション・パッケージを作成するためのオプション +help.option-group.package-platform=アプリケーション・パッケージを作成するためのプラットフォーム依存オプション + +help.option.argument-file=\ ファイルからの読取りオプションまたはモード(あるいはその両方)\n このオプションは複数回使用できます。 + +help.option.about-url=\ アプリケーションのホームページのURL + +help.option.add-launcher.win=\ ランチャの名前、およびキー、値のペアのリスト\n を含むプロパティ・ファイルへのパス\n (絶対パスまたは現在のディレクトリからの相対パス)\n キー"arguments"、"description"、"icon"、"java-options"、\n "launcher-as-service"、"main-class"、"main-jar"、"module"、\n "win-console"、"win-menu"、"win-shortcut"が使用できます。\n これらのオプションを元のコマンドライン・オプションに追加するか、これらのオプションを\n 使用して元のコマンドライン・オプションを上書きして、追加の代替ランチャを作成します。\n メイン・アプリケーション・ランチャはコマンドライン・オプションから作成されます。\n このオプションを使用して追加の代替ランチャを作成でき、\n このオプションを複数回使用して\n 複数の追加のランチャを作成できます。 + +help.option.add-launcher.linux=\ ランチャの名前、およびキー、値のペアのリスト\n を含むプロパティ・ファイルへのパス\n (絶対パスまたは現在のディレクトリからの相対パス)\n キー"arguments"、"description"、"icon"、"java-options"、\n "launcher-as-service"、"linux-shortcut"、"main-class"、"main-jar"、\n "module"が使用できます。\n これらのオプションを元のコマンドライン・オプションに追加するか、これらのオプションを\n 使用して元のコマンドライン・オプションを上書きして、追加の代替ランチャを作成します。\n メイン・アプリケーション・ランチャはコマンドライン・オプションから作成されます。\n このオプションを使用して追加の代替ランチャを作成でき、\n このオプションを複数回使用して\n 複数の追加のランチャを作成できます。 + +help.option.add-launcher.mac=\ ランチャの名前、およびキー、値のペアのリスト\n を含むプロパティ・ファイルへのパス\n (絶対パスまたは現在のディレクトリからの相対パス)\n キー"arguments"、"description"、"icon"、"java-options"、\n "launcher-as-service"、"main-class"、"main-jar"、"module"\n が使用できます。\n これらのオプションを元のコマンドライン・オプションに追加するか、これらのオプションを\n 使用して元のコマンドライン・オプションを上書きして、追加の代替ランチャを作成します。\n メイン・アプリケーション・ランチャはコマンドライン・オプションから作成されます。\n このオプションを使用して追加の代替ランチャを作成でき、\n このオプションを複数回使用して\n 複数の追加のランチャを作成できます。 + +help.option.add-modules=\ 追加するモジュールのカンマ(",")区切りリスト\n このモジュール・リストとメイン・モジュール(指定した場合)\n が--add-module引数としてjlinkに渡されます。\n 指定しなかった場合、メイン・モジュールのみ(--moduleが\n 指定された場合)、またはデフォルトのモジュール・セット(--main-jarが\n 指定された場合)が使用されます。\n このオプションは複数回使用できます。 + +help.option.app-content=\ アプリケーション・ペイロードに追加するファイルまたはディレクトリ(あるいはその両方)\n のパスのカンマ区切りのリスト。\n このオプションは複数回使用できます。 + +help.option.app-content.mac=\ アプリケーション・ペイロードに追加するファイルまたはディレクトリ(あるいはその両方)\n のパスのカンマ区切りのリスト。\n このオプションは複数回使用できます。\n ノート: 値は"Resources"サブディレクトリ\n (またはアプリケーション・バンドルの"Contents"ディレクトリで有効なその他のディレクトリ)\n を含むディレクトリである必要があります。それ以外の場合は、jpackageによって無効なアプリケーション・バンドルが生成される場合があり、\n コード署名または公証(あるいはその両方)に失敗する\n 場合があります。 + +help.option.app-image=\ インストール可能なパッケージの作成に使用する、事前定義済\n アプリケーション・イメージの場所\n (絶対パスまたは現在のディレクトリからの相対パス) + +help.option.app-image.mac=\ インストール可能なパッケージの作成または事前定義済\n アプリケーション・イメージの署名に使用する、事前定義済\n アプリケーション・イメージの場所\n (絶対パスまたは現在のディレクトリからの相対パス) + +help.option.app-version=\ アプリケーションおよびパッケージのバージョン + +help.option.arguments=\ コマンドライン引数がランチャに指定されていない場合に、\n メイン・クラスに渡されるコマンドライン引数\n このオプションは複数回使用できます。 + +help.option.copyright=\ アプリケーションのコピーライト + +help.option.description=\ アプリケーションの説明 + +help.option.dest=\ 生成された出力ファイルが配置されるパス\n (絶対パスまたは現在のディレクトリからの相対パス)\n 現在の作業ディレクトリにデフォルト設定されています。 + +help.option.file-associations=\ キー、値のペアのリストを含むプロパティ・ファイルへのパス\n (絶対パスまたは現在のディレクトリからの相対パス)\n キー"extension"、"mime-type"、"icon"、"description"\n を使用して関連付けを記述できます。\n このオプションは複数回使用できます。 + +help.option.help=\ 使用方法テキストと現在のプラットフォームの有効なオプションのリストと説明を\n 出力ストリームに出力して、終了します + +help.option.icon=\ アプリケーション・パッケージのアイコンのパス\n (絶対パスまたは現在のディレクトリからの相対パス) + +help.option.input=\ パッケージ化するファイルを含む入力ディレクトリのパス\n (絶対パスまたは現在のディレクトリからの相対パス)\n 入力ディレクトリのすべてのファイルは、アプリケーション・イメージに\n パッケージ化されます。 + +help.option.install-dir=\ アプリケーションのインストール・ディレクトリの絶対パス + +help.option.install-dir.win=\ "プログラム・ファイル"または"AppData"など、\n アプリケーションのインストール場所の相対サブパス。 + +help.option.installer-runtime-image=\ インストールする事前定義済のランタイム・イメージのパス\n (絶対パスまたは現在のディレクトリからの相対パス)\n ランタイム・パッケージの作成時には、オプションが必要です。 + +help.option.java-options=\ Javaランタイムに渡すオプション\n このオプションは複数回使用できます。 + +help.option.jlink-options=\ jlinkに渡すオプションのスペース区切りのリスト\n 指定しない場合、"--strip-native-commands\n --strip-debug --no-man-pages --no-header-files"にデフォルト設定されます。\n このオプションは複数回使用できます。 + +help.option.launcher-as-service=\ バックグラウンド・サービス・タイプ・アプリケーションとしてメイン・\n アプリケーション・ランチャを登録するインストーラの作成をリクエストします。 + +help.option.license-file=\ ライセンス・ファイルへのパス\n (絶対パスまたは現在のディレクトリからの相対パス) + +help.option.linux-app-category=\ RPM .specファイルのグループ値または\n DEB制御ファイルのセクション値 + +help.option.linux-app-release=\ RPM .specファイルのリリース値または\n DEB制御ファイルのDebianリビジョン値 + +help.option.linux-deb-maintainer=\ .debパッケージのメンテナ + +help.option.linux-menu-group=\ このアプリケーションを配置するメニュー・グループ + +help.option.linux-package-deps=\ アプリケーションに必要なパッケージまたは機能 + +help.option.linux-package-name=\ Linuxパッケージの名前。アプリケーション名にデフォルト設定されています + +help.option.linux-rpm-license-type=\ ライセンスのタイプ(RPM .specの"License: ") + +help.option.linux-shortcut=\ アプリケーションのショートカットを作成します。 + +help.option.mac-app-category=\ アプリケーションplistのLSApplicationCategoryTypeの構築に\n 使用する文字列。デフォルト値は"utilities"です。 + +help.option.mac-app-image-sign-identity=\ アプリケーション・イメージの署名に使用するアイデンティティ。この値は直接\n "codesign"ツールの--signオプションに渡されます。このオプションは\n --mac-signing-key-user-nameと組み合せることはできません。 + +help.option.mac-app-store=\ jpackage出力がMac App Store用であること\n を示します。 + +help.option.mac-dmg-content=\ dmgに参照されたコンテンツをすべて含めます。\n このオプションは複数回使用できます。 + +help.option.mac-entitlements=\ バンドルの実行可能ファイルおよびライブラリの署名時に\n 使用する権限を含むファイルのパス。 + +help.option.mac-installer-sign-identity=\ "pkg"インストーラの署名に使用するアイデンティティ。この値は直接\n "productbuild"ツールの--signオプションに渡されます。このオプションは\n --mac-signing-key-user-nameと組み合せることはできません。 + +help.option.mac-package-identifier=\ MacOSのアプリケーションを一意に識別する識別子。\n メイン・クラス名にデフォルト設定されています。\n 英数字(A-Z、a-z、0-9)、ハイフン(-)、\n およびピリオド(.)のみを使用できます。 + +help.option.mac-package-name=\ メニュー・バーに表示されるアプリケーションの名前\n アプリケーション名とは異なります。\n この名前は16文字未満にする必要があり、メニュー・バー\n およびアプリケーション情報ウィンドウに表示するのに適している必要があります。\n アプリケーション名にデフォルト設定されています。 + +help.option.mac-package-signing-prefix=\ アプリケーション・パッケージに署名する際、既存のパッケージIDのない\n 署名が必要なすべてのコンポーネントに、\n この値が接頭辞として付けられます。 + +help.option.mac-sign=\ パッケージまたは事前定義済アプリケーション・イメージに署名するよう\n リクエストします。 + +help.option.mac-signing-keychain=\ 署名アイデンティティを検索するキーチェーンの名前\n 指定しなかった場合、標準のキーチェーンが使用されます。 + +help.option.mac-signing-key-user-name=\ Apple署名アイデンティティのチームまたはユーザー名部分。\n アプリケーション・イメージまたはインストーラの署名に使用する署名アイデンティティの\n 直接制御には、--mac-app-image-sign-identityまたは\n --mac-installer-sign-identity(あるいはその両方)を使用します。このオプションは\n --mac-app-image-sign-identityまたは--mac-installer-sign-identityと組み合せることはできません。 + +help.option.main-class=\ 実行するアプリケーション・メイン・クラスの修飾名\n このオプションを使用できるのは、--main-jarが指定されている場合だけです。 + +help.option.main-jar=\ メイン・クラスを含む、アプリケーションのメインJAR\n (入力パスからの相対パスとして指定)\n --moduleまたは--main-jarオプションを指定できますが、両方は\n 指定できません。 + +help.option.module=\ アプリケーションのメイン・モジュール(およびオプションでメイン・クラス)\n このモジュールは、モジュール・パスに置かれている必要があります。\n このオプションが指定されている場合、メイン・モジュールは\n Javaランタイム・イメージ内でリンクされます。--moduleまたは--main-jar\n オプションを指定できますが、両方は指定できません。 + +help.option.module-path=\ :で区切られたパスのリスト\n 各パスは、モジュールのディレクトリまたは\n モジュラjarへのパスです。\n (各パスは、絶対パスまたは現在のディレクトリからの相対パスです。)\n このオプションは複数回使用できます。 +help.option.module-path.win=\ ;で区切られたパスのリスト\n 各パスは、モジュールのディレクトリまたは\n モジュラjarへのパスです。\n (各パスは、絶対パスまたは現在のディレクトリからの相対パスです。)\n このオプションは複数回使用できます。 + +help.option.name=\ アプリケーションおよびパッケージの名前 + +help.option.resource-dir=\ オーバーライドjpackageリソースへのパス\n アイコン、テンプレート・ファイルおよびjpackageのその他のリソースは、\n このディレクトリに置換リソースを追加することでオーバーライドできます。\n (絶対パスまたは現在のディレクトリからの相対パス) + +help.option.runtime-image=\ アプリケーション・イメージにコピーされる、事前定義済のランタイム・イメージ\n のパス\n (絶対パスまたは現在のディレクトリからの相対パス)\n --runtime-imageが指定されていない場合、jpackageはjlinkを実行し、\n 次のオプションを使用してランタイム・イメージを作成します:\n --strip-debug、--no-header-files、--no-man-pagesおよび\n --strip-native-commands。 + +help.option.temp=\ 一時ファイルの作成に使用される新規または空のディレクトリのパス\n (絶対パスまたは現在のディレクトリからの相対パス)\n 指定した場合、タスク完了時に一時ディレクトリは削除されないため\n 手動で削除する必要があります。\n 指定しなかった場合、一時ディレクトリが作成され\n タスク完了時に削除されます。 + +help.option.type.win=\ 作成するパッケージのタイプ\n 有効な値: {"app-image", "exe", "msi"}\n このオプションを指定しない場合、プラットフォーム依存の\n デフォルト・タイプが作成されます。 +help.option.type.linux=\ 作成するパッケージのタイプ\n 有効な値: {"app-image", "deb", "rpm"}\n このオプションを指定しない場合、プラットフォーム依存の\n デフォルト・タイプが作成されます。 +help.option.type.mac=\ 作成するパッケージのタイプ\n 有効な値: {"app-image", "dmg", "pkg"}\n このオプションを指定しない場合、プラットフォーム依存の\n デフォルト・タイプが作成されます。 + +help.option.vendor=\ アプリケーションのベンダー + +help.option.verbose=\ 詳細な出力を有効にします + +help.option.version=\ 製品バージョンを出力ストリームに出力して終了します + +help.option.win-console=\ アプリケーションのコンソール・ランチャを作成します。コンソール・\n インタラクションが必要なアプリケーションに指定する必要があります + +help.option.win-dir-chooser=\ 製品をインストールするディレクトリをユーザーが\n 選択できるダイアログを追加します。 + +help.option.win-help-url=\ ユーザーが詳細情報または技術サポートを取得できるURL + +help.option.win-menu=\ このアプリケーションのスタート・メニューのショートカットを追加するリクエスト + +help.option.win-menu-group=\ このアプリケーションを配置するスタート・メニュー・グループ + +help.option.win-per-user-install=\ ユーザーごとにインストールを実行するリクエスト + +help.option.win-shortcut=\ このアプリケーションのデスクトップのショートカットを追加するリクエスト + +help.option.win-shortcut-prompt=\ ショートカットをインストーラで作成するかどうかをユーザーが\n 選択できるダイアログを追加します。 + +help.option.win-update-url=\ 使用可能なアプリケーション更新情報のURL + +help.option.win-upgrade-uuid=\ このパッケージのアップグレードに関連付けられているUUID -MSG_Help_win_launcher=\nアプリケーション・ランチャを作成するためのプラットフォーム依存オプション:\n --win-console\n アプリケーションのコンソール・ランチャを作成します。コンソール・\n インタラクションが必要なアプリケーションに指定する必要があります\n -MSG_Help_win_install=\ --win-dir-chooser\n ユーザーが製品をインストールするディレクトリを選択するための\n ダイアログを追加します。\n --win-help-url \n ユーザーが詳細情報または技術的なサポートを取得できるURL\n --win-menu\n このアプリケーションのスタート・メニュー・ショートカットを追加するようにリクエストします\n --win-menu-group \n このアプリケーションを配置するスタート・メニュー・グループ\n --win-per-user-install\n ユーザーごとにインストールを実行するようにリクエストします\n --win-shortcut\n このアプリケーションのデスクトップ・ショートカットを追加するようにリクエストします\n --win-shortcut-prompt\n ショートカットをインストーラで作成するかどうかをユーザーが選択できるようにする\n ダイアログを追加します。\n --win-update-url \n 使用可能なアプリケーションの更新情報のURL\n --win-upgrade-uuid \n このパッケージのアップグレードに関連付けられたUUID\n -MSG_Help_win_install_dir=デフォルトのインストール場所の下の相対サブパス\n -MSG_Help_mac_install=\ --mac-dmg-content [,...]\n dmgに参照されたコンテンツをすべて含めます。\n このオプションは複数回使用できます。 \n -MSG_Help_mac_launcher=\ --mac-package-identifier \n macOSのアプリケーションを一意に識別するID\n メイン・クラス名にデフォルト設定されています。\n 英数字(A-Z、a-z、0-9)、ハイフン(-)およびピリオド(.)文字のみ\n 使用できます。\n --mac-package-name \n メニュー・バーに表示されるアプリケーションの名前\n アプリケーション名とは異なります。\n この名前は16文字未満にする必要があり、メニュー・バーおよびアプリケーション情報ウィンドウに\n 表示するのに適している必要があります。\n アプリケーション名にデフォルト設定されています。\n --mac-package-signing-prefix \n アプリケーション・パッケージに署名する際、\n 既存のパッケージIDのない署名が必要なすべてのコンポーネントに、\n この値が接頭辞として付けられます。\n --mac-sign\n パッケージまたは事前定義済アプリケーション・イメージに署名するよう\n リクエストします。\n --mac-signing-keychain \n 署名アイデンティティを検索するキーチェーンの名前\n 指定しなかった場合、標準のキーチェーンが使用されます。\n --mac-signing-key-user-name \n Apple署名アイデンティティのチームまたはユーザー名部分。\n アプリケーション・イメージまたはインストーラの署名に使用する署名アイデンティティの\n 直接制御には、--mac-app-image-sign-identityまたは\n --mac-installer-sign-identity(あるいは両方)を使用します。このオプションは\n --mac-app-image-sign-identityまたは--mac-installer-sign-identityと組み合せることはできません。\n --mac-app-image-sign-identity \n アプリケーション・イメージの署名に使用するアイデンティティ。この値は直接\n "codesign"ツールの--signオプションに渡されます。このオプションは\n \ ---mac-signing-key-user-nameと組み合せることはできません。\n --mac-installer-sign-identity \n "pkg"インストーラの署名に使用するアイデンティティ。この値は直接\n "productbuild"ツールの--signオプションに渡されます。このオプションは\n --mac-signing-key-user-nameと組み合せることはできません。\n --mac-app-store\n jpackage出力がMac App Store用であること\n を示します。\n --mac-entitlements \n バンドルの実行可能ファイルおよびライブラリの署名時に\n 使用する権限を含むファイルのパス。\n --mac-app-category \n アプリケーションのplistのLSApplicationCategoryTypeを生成する際に使用する文字列。\n デフォルト値は"utilities"です。\n -MSG_Help_linux_install=\ --linux-package-name \n Linuxパッケージの名前。アプリケーション名にデフォルト設定されています\n --linux-deb-maintainer \n .debパッケージのMaintainer\n --linux-menu-group \n このアプリケーションが配置されているメニュー・グループ\n --linux-package-deps \n アプリケーションに必要なパッケージまたは機能\n --linux-rpm-license-type \n ライセンスのタイプ(RPM .specの"License: ")\n --linux-app-release \n RPM .specファイルのリリース値または\n DEBコントロール・ファイルのDebianリビジョン値。\n --linux-app-category \n RPM .specファイルのグループ値または \n DEBコントロール・ファイルのセクション値\n --linux-shortcut\n アプリケーションのショートカットを作成します。\n -MSG_Help_mac_linux_install_dir=アプリケーションのインストール・ディレクトリの絶対パス\n -MSG_Help_default_install_dir=OS XまたはLinux上のアプリケーションのインストール・ディレクトリの絶対パス。\n "プログラム・ファイル"や"AppData"など、Windows上のアプリケーションの\n インストール場所の相対サブパス。\n -MSG_Help_no_args=使用方法: jpackage \n利用可能なオプションのリストについては、jpackage --help (or -h)を使用します -MSG_Help_default_app_image=インストール可能なパッケージの作成に使用する、事前定義済み\n アプリケーション・イメージの場所\n -MSG_Help_mac_app_image=インストール可能なパッケージの作成または事前定義済みアプリケーション・\n イメージへの署名に使用する、事前定義済みアプリケーション・\n イメージの場所\n -MSG_Help_mac_sign_sample_usage=\ 事前定義済みアプリケーション・イメージへの署名:\n jpackage --type app-image --app-image \\\n --mac-sign [...]\n ノート: このモードで許可される唯一の追加オプション:\n 追加のmac署名オプションのセットおよび--verbose\n diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties index 6adf62ef1f8..ac72c67ee2f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -24,20 +24,163 @@ # # -MSG_Help=用法:jpackage \n\n示例用法:\n--------------\n 生成适合主机系统的应用程序包:\n 对于模块化应用程序:\n jpackage -n name -p modulePath -m moduleName/className\n 对于非模块化应用程序:\n jpackage -i inputDir -n name \\\n --main-class className --main-jar myJar.jar\n 从预构建的应用程序映像:\n jpackage -n name --app-image appImageDir\n 生成应用程序映像:\n 对于模块化应用程序:\n jpackage --type app-image -n name -p modulePath \\\n -m moduleName/className\n 对于非模块化应用程序:\n jpackage --type app-image -i inputDir -n name \\\n --main-class className --main-jar myJar.jar\n 要为 jlink 提供您自己的选项,请单独运行 jlink:\n jlink --output appRuntimeImage -p modulePath \\\n --add-modules moduleName \\\n --no-header-files [...]\n jpackage --type app-image -n name \\\n -m moduleName/className --runtime-image appRuntimeImage\n 生成 Java 运行时程序包:\n jpackage -n name --runtime-image \n{6}\n一般选项:\n @ \n 从文件读取选项和/或模式 \n 可以多次使用此选项。\n --type -t \n 要创建的程序包的类型\n 有效值为:{1} \n 如果未指定此选项,则将创建与平台相关的\n 默认类型。\n --app-version \n 应用程序和/或程序包的版本\n --copyright \n 应用程序的版权\n --description \n 应用程序的说明\n --help -h \n 将用法文本输出到输出流并退出,用法文本中包含\n 适用于当前平台的每个有效选项的列表和说明\n --icon \n 应用程序包图标的路径\n (绝对路径或相对于当前目录的路径)\n --name -n \n 应用程序和/或程序包的名称\n --dest -d \n 用来放置所生成的输出文件的路径\n (绝对路径或相对于当前目录的路径)\n 默认为当前的工作目录。\n --temp \n 用来创建临时文件的新目录或空白目录的路径\n (绝对路径或相对于当前目录的路径)\n 如果指定,则在任务完成时将不删除临时目录,\n 必须手动删除临时目录。\n 如果未指定,则将创建一个临时目录,\n \ -并在任务完成时删除该临时目录。\n --vendor \n 应用程序的供应商\n --verbose\n 启用详细的输出\n --version\n 将产品版本输出到输出流并退出。\n\n用来创建运行时映像的选项:\n --add-modules <模块名称>[,<模块名称>...]\n 要添加的模块的逗号 (",") 分隔列表\n 此模块列表连同主模块(如果指定)\n 将作为 --add-module 参数传递到 jlink。\n 如果未指定,则仅使用主模块(如果指定了 --module),\n 或者使用默认的模块集(如果指定了 \n --main-jar)。\n 可以多次使用此选项。\n --module-path -p ...\n 路径的 {0} 分隔列表\n 每个路径要么是模块的目录,要么是\n 模块化 jar 的路径。\n (每个路径可以是绝对路径,也可以是相对于当前目录的路径。)\n 可以多次使用此选项。\n --jlink-options \n 要传递给 jlink 的选项列表(用空格分隔) \n 如果未指定,则默认为 "--strip-native-commands \n --strip-debug --no-man-pages --no-header-files"。 \n 可以多次使用此选项。\n --runtime-image \n 将复制到应用程序映像的预定义\n 运行时映像的路径\n (绝对路径或相对于当前目录的路径)\n 如果未指定 --runtime-image,jpackage 将运行 jlink 以\n 使用如下选项创建运行时映像:\n --strip-debug、--no-header-files、--no-man-pages 和 \n --strip-native-commands。\n\n用来创建应用程序映像的选项:\n --input -i \n 包含要打包的文件的输入目录的路径\n (绝对路径或相对于当前目录的路径)\n 输入目录中的所有文件将打包到\n 应用程序映像中。\n --app-content [,...]\n 要添加到应用程序有效负载中的文件和/或\n 目录的逗号分隔路径列表。\n 此选项可以多次使用。\n\n用来创建应用程序启动程序的选项:\n --add-launcher =\n 启动程序的名称和包含关键字-值对列表的\n 属性文件的路径\n (绝对路径或相对于当前目录的路径)\n \ -可以使用关键字 "module"、"main-jar"、"main-class"、"description"、\n "arguments"、"java-options"、"app-version"、"icon"、\n "launcher-as-service"、\n "win-console"、"win-shortcut"、"win-menu"、\n "linux-app-category" 和 "linux-shortcut"。\n 这些选项将添加到原始命令行选项中或者用来覆盖\n 原始命令行选项,以构建额外的替代启动程序。\n 将从命令行选项构建主应用程序启动程序。\n 可以使用此选项构建额外的替代启动程序,\n 可以多次使用此选项来构建\n 多个额外的启动程序。 \n --arguments
\n 在没有为启动程序提供命令行参数时,\n 要传递到主类的命令行参数\n 可以多次使用此选项。\n --java-options \n 要传递到 Java 运行时的选项\n 可以多次使用此选项。\n --main-class \n 要执行的应用程序主类的限定名称\n 只有在指定了 --main-jar 时才能使用此选项。\n --main-jar
\n 应用程序的主 JAR;包含主类\n (指定为相对于输入路径的路径)\n 可以指定 --module 或 --main-jar 选项,但是不能同时指定\n 两者。\n --module -m [/
]\n 应用程序的主模块(以及可选的主类)\n 此模块必须位于模块路径中。\n 如果指定了此选项,则将在 Java 运行时映像中\n 链接主模块。可以指定 --module 或 --main-jar 选项,\n 但是不能同时指定这两个选项。\n{2}\n用来创建应用程序包的选项:\n --about-url \n 应用程序主页的 URL\n --app-image \n {5} (绝对路径或相对于当前目录的路径)\n --file-associations \n 包含关键字-值对列表的属性文件的路径\n (绝对路径或相对于当前目录的路径)\n 可以使用关键字 "extension"、"mime-type"、"icon" 和 "description" \n 来描述此关联。\n 可以多次使用此选项。\n --install-dir \n {4} --license-file \n 许可证文件的路径\n (绝对路径或相对于当前目录的路径)\n --resource-dir \n 覆盖 jpackage 资源的路径\n \ -可以通过向该目录中添加替代资源来覆盖 jpackage 的\n 图标、模板文件和其他资源。\n (绝对路径或相对于当前目录的路径)\n --runtime-image \n 要安装的预定义运行时映像的路径\n (绝对路径或相对于当前目录的路径)\n 在创建运行时程序包时需要使用选项。\n --launcher-as-service\n 请求创建安装程序,以将主\n 应用程序启动程序注册为后台服务类型应用程序。\n\n用来创建应用程序包的与平台相关的选项:\n{3} +help.header=用法:jpackage + +help.short=使用 jpackage --help(或 -h)可获取可能选项的列表 + +help.option-group.sample.create-native-package=\ 生成适用于主机系统的应用程序包:\n 对于模块化应用程序:\n jpackage -n name -p modulePath -m moduleName/className\n 对于非模块化应用程序:\n jpackage -i inputDir -n name \\\n --main-class className --main-jar myJar.jar\n 从预构建的应用程序映像:\n jpackage -n name --app-image appImageDir + +help.option-group.sample.create-app-image=\ 生成应用程序映像:\n 对于模块化应用程序:\n jpackage --type app-image -n name -p modulePath \\\n -m moduleName/className\n 对于非模块化应用程序:\n jpackage --type app-image -i inputDir -n name \\\n --main-class className --main-jar myJar.jar\n 要向 jlink 提供您自己的选项,请分别运行 jlink:\n jlink --output appRuntimeImage -p modulePath \\\n --add-modules moduleName \\\n --no-header-files [...]\n jpackage --type app-image -n name \\\n -m moduleName/className --runtime-image appRuntimeImage + +help.option-group.sample.create-runtime-installer=\ 生成 Java 运行时程序包:\n jpackage -n name --runtime-image + +help.option-group.sample.sign-app-image=\ 对预定义应用程序映像进行签名:\n jpackage --type app-image --app-image \\\n --mac-sign [...]\n 注:此模式下允许的其他选项只有:\n 一组其他 mac 签名选项和 --verbose + +help.option-group.sample=示例用法 +help.option-group.generic=一般选项 +help.option-group.runtime-image=用来创建运行时映像的选项 +help.option-group.app-image=用来创建应用程序映像的选项 +help.option-group.launcher=用来创建应用程序启动程序的选项 +help.option-group.launcher-platform=用来创建应用程序启动程序的平台相关选项 +help.option-group.package=用来创建应用程序包的选项 +help.option-group.package-platform=用来创建应用程序包的与平台相关的选项 + +help.option.argument-file=\ 从文件读取选项和/或模式\n 可以多次使用此选项。 + +help.option.about-url=\ 应用程序主页的 URL + +help.option.add-launcher.win=\ 启动程序的名称,以及包含\n 键值对列表的属性文件的路径\n (绝对路径或相对于当前目录的路径)\n 可以使用键 "arguments"、"description"、"icon"、"java-options"、\n "launcher-as-service"、"main-class"、"main-jar"、"module"、\n "win-console"、"win-menu" 和 "win-shortcut"。\n 这些选项添加到或用于覆盖原始\n 命令行选项以构建其他替代启动程序。\n 将从命令行选项构建主应用程序启动程序。\n 可以使用此选项构建其他替代启动程序,\n 并且可以多次使用此选项\n 构建多个其他启动程序。 + +help.option.add-launcher.linux=\ 启动程序的名称,以及包含\n 键值对列表的属性文件的路径\n (绝对路径或相对于当前目录的路径)\n 可以使用键 "arguments"、"description"、"icon"、"java-options"、\n "launcher-as-service"、"linux-shortcut"、"main-class"、"main-jar" \n 和 "module"。\n 这些选项添加到或用于覆盖原始\n 命令行选项以构建其他替代启动程序。\n 将从命令行选项构建主应用程序启动程序。\n 可以使用此选项构建其他替代启动程序,\n 并且可以多次使用此选项\n 构建多个其他启动程序。 + +help.option.add-launcher.mac=\ 启动程序的名称,以及包含\n 键值对列表的属性文件的路径\n (绝对路径或相对于当前目录的路径)\n 可以使用键 "arguments"、"description"、"icon"、"java-options"、\n "launcher-as-service"、"main-class"、"main-jar" 和\n "module"。\n 这些选项添加到或用于覆盖原始\n 命令行选项以构建其他替代启动程序。\n 将从命令行选项构建主应用程序启动程序。\n 可以使用此选项构建其他替代启动程序,\n 并且可以多次使用此选项\n 构建多个其他启动程序。 + +help.option.add-modules=\ 要添加的模块列表(以逗号 (",") 分隔)\n 此模块列表以及主模块(如果指定)\n 将作为 --add-module 参数传递到 jlink。\n 如果未指定,则仅使用主模块\n (如果指定 --module)或默认模块集\n (如果指定 --main-jar)。\n 可以多次使用此选项。 + +help.option.app-content=\ 要添加到应用程序有效负载中的文件\n 和/或目录路径的逗号分隔列表。\n 可以多次使用此选项。 + +help.option.app-content.mac=\ 要添加到应用程序有效负载中的文件\n 和/或目录路径的逗号分隔列表。\n 可以多次使用此选项。\n 注:该值应为具有 "Resources" 子目录的\n 目录(或应用程序包的 "Contents" 目录中\n 有效的任何其他目录)。否则,jpackage \n 可能会生成无效的应用程序包,此应用程序包\n 可能会使代码签名和/或公证失败。 + +help.option.app-image=\ 用来构建可安装程序包的\n 预定义应用程序映像的位置\n (绝对路径或相对于当前目录的路径) + +help.option.app-image.mac=\ 用来构建可安装程序包或对预定义应用程序\n 映像进行签名的预定义应用程序\n 映像的位置\n (绝对路径或相对于当前目录的路径) + +help.option.app-version=\ 应用程序和/或程序包的版本 + +help.option.arguments=\ 在没有为启动程序提供命令行参数时,\n 要传递到主类的命令行参数\n 可以多次使用此选项。 + +help.option.copyright=\ 应用程序的版权 + +help.option.description=\ 应用程序的说明 + +help.option.dest=\ 放置生成的输出文件的路径\n (绝对路径或相对于当前目录的路径)\n 默认为当前工作目录。 + +help.option.file-associations=\ 包含键值对列表的属性文件的路径\n (绝对路径或相对于当前目录的路径)\n 键 "extension"、"mime-type"、"icon" 和 "description"\n 可用于描述关联。\n 可以多次使用此选项。 + +help.option.help=\ 将用法文本输出到输出流并退出,用法文本中包含\n 适用于当前平台的每个有效选项的列表和说明 + +help.option.icon=\ 应用程序包图标的路径\n (绝对路径或相对于当前目录的路径) + +help.option.input=\ 包含要打包的文件的输入目录的路径\n (绝对路径或相对于当前目录的路径)\n 输入目录中的所有文件将打包到\n 应用程序映像中。 + +help.option.install-dir=\ 应用程序安装目录的绝对路径 + +help.option.install-dir.win=\ 应用程序安装位置的相对子路径\n 例如 "Program Files" 或 "AppData"。 + +help.option.installer-runtime-image=\ 要安装的预定义运行时映像的路径\n (绝对路径或相对于当前目录的路径)\n 创建运行时程序包时需要提供选项。 + +help.option.java-options=\ 传递到 Java 运行时的选项\n 可以多次使用此选项。 + +help.option.jlink-options=\ 要传递到 jlink 的选项列表(以空格分隔)\n 如果未指定,则默认为 "--strip-native-commands\n --strip-debug --no-man-pages --no-header-files"。\n 可以多次使用此选项。 + +help.option.launcher-as-service=\ 请求创建安装程序,以将主应用程序\n 启动程序注册为后台服务类型应用程序。 + +help.option.license-file=\ 许可证文件的路径\n (绝对路径或相对于当前目录的路径) + +help.option.linux-app-category=\ RPM .spec 文件的组值或\n DEB 控制文件的节值 + +help.option.linux-app-release=\ RPM .spec 文件的发行版值或\n DEB 控制文件的 Debian 修订版值 + +help.option.linux-deb-maintainer=\ .deb 程序包的维护程序 + +help.option.linux-menu-group=\ 此应用程序所在的菜单组 + +help.option.linux-package-deps=\ 应用程序所需的程序包或功能 + +help.option.linux-package-name=\ Linux 程序包的名称,默认为应用程序名称 + +help.option.linux-rpm-license-type=\ 许可证类型(RPM .spec 的 "License: ") + +help.option.linux-shortcut=\ 为应用程序创建快捷方式。 + +help.option.mac-app-category=\ 用于构造应用程序 plist 中的 LSApplicationCategoryType 的\n 字符串。默认值为 "utilities"。 + +help.option.mac-app-image-sign-identity=\ 用于对应用程序映像进行签名的身份。此值将\n 直接传递到 "codesign" 工具的 --sign 选项。此选项\n 不能与 --mac-signing-key-user-name 结合使用。 + +help.option.mac-app-store=\ 指示 jpackage 输出面向\n Mac App Store。 + +help.option.mac-dmg-content=\ 包括 dmg 中引用的所有内容。\n 可以多次使用此选项。 + +help.option.mac-entitlements=\ 包含对包中的可执行文件和库进行签名时\n 要使用的权利的文件路径。 + +help.option.mac-installer-sign-identity=\ 用于对 "pkg" 安装程序进行签名的身份。此值将\n 直接传递到 "productbuild" 工具的 --sign 选项。此选项\n 不能与 --mac-signing-key-user-name 结合使用。 + +help.option.mac-package-identifier=\ 唯一标识 macOS 应用程序的标识符\n 默认为主类名称。\n 只能使用字母数字 (A-Z,a-z,0-9)、连字符 (-)\n 和句点 (.) 字符。 + +help.option.mac-package-name=\ 应用程序在菜单栏中显示的名称\n 此名称可以与应用程序名称不同。\n 此名称的长度必须少于 16 个字符,并且\n 适合显示在菜单栏和应用程序“信息”窗口中。\n 默认为应用程序名称。 + +help.option.mac-package-signing-prefix=\ 对应用程序包进行签名时,此值将作为\n 前缀添加到需要签名但当前没有程序包\n 标识符的所有组件。 + +help.option.mac-sign=\ 请求对程序包或预定义的应用程序映像\n 进行签名。 + +help.option.mac-signing-keychain=\ 要用来搜索签名身份的密钥链的名称\n 如果未指定,则使用标准密钥链。 + +help.option.mac-signing-key-user-name=\ Apple 签名身份的团队或用户名称部分。为了直接\n 控制用于对应用程序映像或安装程序进行签名的\n 签名身份,请使用 --mac-app-image-sign-identity 和/或\n --mac-installer-sign-identity。此选项不能与\n --mac-app-image-sign-identity 或 --mac-installer-sign-identity 结合使用。 + +help.option.main-class=\ 要执行的应用程序主类的限定名称\n 只有在指定了 --main-jar 时才能使用此选项。 + +help.option.main-jar=\ 应用程序的主 JAR;包含主类\n (指定为相对于输入路径的路径)\n 可以指定 --module 或 --main-jar 选项,\n 但不能同时指定这两个选项。 + +help.option.module=\ 应用程序的主模块(以及可选的主类)\n 此模块必须位于模块路径中。\n 指定了此选项时,将在 Java 运行时映像中\n 链接主模块。可以指定 --module 或 --main-jar \n 选项,但不能同时指定这两个选项。 + +help.option.module-path=\ 以 : 分隔的路径列表\n 每个路径是模块的目录或\n 模块化 jar 的路径。\n (每个路径可以是绝对路径,也可以是相对于当前目录的路径。)\n 可以多次使用此选项。 +help.option.module-path.win=\ 以 ; 分隔的路径列表\n 每个路径是模块的目录或\n 模块化 jar 的路径。\n (每个路径可以是绝对路径,也可以是相对于当前目录的路径。)\n 可以多次使用此选项。 + +help.option.name=\ 应用程序和/或程序包的名称 + +help.option.resource-dir=\ 覆盖 jpackage 资源的路径\n 可以通过向此目录中添加替换资源来覆盖 jpackage 的\n 图标、模板文件和其他资源。\n (绝对路径或相对于当前目录的路径) + +help.option.runtime-image=\ 将复制到应用程序映像中的预定义\n 运行时映像的路径\n (绝对路径或相对于当前目录的路径)\n 如果未指定 --runtime-image,jpackage 将运行 jlink 以\n 使用以下选项创建运行时映像:\n --strip-debug、--no-header-files、--no-man-pages 和\n --strip-native-commands。 + +help.option.temp=\ 用于创建临时文件的新目录或空目录的路径\n (绝对路径或相对于当前目录的路径)\n 如果指定,则在任务完成时将不删除\n 临时目录,必须手动删除临时目录。\n 如果未指定,将创建临时目录并\n 并在任务完成时删除该临时目录。 + +help.option.type.win=\ 要创建的程序包的类型\n 有效值为:{"app-image", "exe", "msi"}\n 如果未指定此选项,则将创建与平台相关的\n 默认类型。 +help.option.type.linux=\ 要创建的程序包的类型\n 有效值为:{"app-image", "deb", "rpm"}\n 如果未指定此选项,则将创建与平台相关的\n 默认类型。 +help.option.type.mac=\ 要创建的程序包的类型\n 有效值为:{"app-image", "dmg", "pkg"}\n 如果未指定此选项,则将创建与平台相关的\n 默认类型。 + +help.option.vendor=\ 应用程序的供应商 + +help.option.verbose=\ 启用详细的输出 + +help.option.version=\ 将产品版本输出到输出流并退出。 + +help.option.win-console=\ 为应用程序创建控制台启动程序,应当为\n 需要控制台交互的应用程序指定 + +help.option.win-dir-chooser=\ 添加一个对话框以允许用户选择\n 产品的安装目录。 + +help.option.win-help-url=\ 用户可以从中获取更多信息或技术支持的 URL + +help.option.win-menu=\ 请求为此应用程序添加“开始”菜单快捷方式 + +help.option.win-menu-group=\ 此应用程序所在的“开始”菜单组 + +help.option.win-per-user-install=\ 请求基于每个用户执行安装 + +help.option.win-shortcut=\ 请求为此应用程序添加桌面快捷方式 + +help.option.win-shortcut-prompt=\ 添加一个对话框以允许用户选择是否将由安装程序\n 创建快捷方式。 + +help.option.win-update-url=\ 可用应用程序更新信息的 URL + +help.option.win-upgrade-uuid=\ 与此程序包的升级关联的 UUID -MSG_Help_win_launcher=\n用来创建应用程序启动程序的与平台相关的选项:\n --win-console\n 为应用程序创建控制台启动程序,应当为\n 需要控制台交互的应用程序指定\n -MSG_Help_win_install=\ --win-dir-chooser\n 添加一个对话框以允许用户选择\n 产品的安装目录。\n --win-help-url \n 用户可以从中获取更多信息或技术支持的 URL\n --win-menu\n 请求为此应用程序添加开始菜单快捷方式\n --win-menu-group \n 此应用程序所在的开始菜单组\n --win-per-user-install\n 请求基于每个用户执行安装\n --win-shortcut\n 请求为此应用程序添加桌面快捷方式\n --win-shortcut-prompt\n 添加一个对话框以允许用户选择是否将由安装程序\n 创建快捷方式。\n --win-update-url \n 可用应用程序更新信息的 URL\n --win-upgrade-uuid \n 与此程序包的升级相关联的 UUID\n -MSG_Help_win_install_dir=默认安装位置下面的相对子路径\n -MSG_Help_mac_install=\ --mac-dmg-content [,...]\n 包括 DMG 中引用的所有内容。\n 此选项可以使用多次。\n -MSG_Help_mac_launcher=\ --mac-package-identifier \n 用来唯一地标识 macOS 应用程序的标识符\n 默认为主类名称。\n 只能使用字母数字 (A-Z,a-z,0-9)、连字符 (-) 和\n 句点 (.) 字符。\n --mac-package-name \n 出现在菜单栏中的应用程序名称\n 这可以与应用程序名称不同。\n 此名称的长度必须小于 16 个字符,适合\n 显示在菜单栏中和应用程序“信息”窗口中。\n 默认为应用程序名称。\n --mac-package-signing-prefix \n 在对应用程序包签名时,会在所有需要签名\n 但当前没有程序包标识符的组件的\n 前面加上此值。\n --mac-sign\n 请求对程序包或预定义的应用程序映像\n 进行签名。\n --mac-signing-keychain \n 要用来搜索签名身份的密钥链的名称\n 如果未指定,则使用标准的密钥链。\n --mac-signing-key-user-name \n Apple 签名身份的团队或用户名称部分。为了直接\n 控制用于对应用程序映像或安装程序进行签名的\n 签名身份,请使用 --mac-app-image-sign-identity 和/或\n --mac-installer-sign-identity。此选项不能与\n --mac-app-image-sign-identity 或 --mac-installer-sign-identity 结合使用。\n --mac-app-image-sign-identity \n 用于对应用程序映像进行签名的身份。此值将直接\n 传递至 "codesign" 工具的 --sign 选项。此选项不能\n 与 --mac-signing-key-user-name 结合使用。\n --mac-installer-sign-identity \n 用于对 "pkg" 安装程序进行签名的身份。此值将直接\n 传递至 "productbuild" 工具的 --sign 选项。此选项不能\n 与 --mac-signing-key-user-name 结合使用。\n --mac-app-store\n 指示 jpackage 输出面向\n Mac App Store。\n --mac-entitlements \n 包含一些权利的文件的路径,在对捆绑包中的可执行文件\n 和库进行签名时会使用这些权利。\n --mac-app-category \n 用于构造应用程序 plist 中 LSApplicationCategoryType 的\n 字符串。默认值为 "utilities"。\n -MSG_Help_linux_install=\ --linux-package-name \n Linux 程序包的名称,默认为应用程序名称\n --linux-deb-maintainer \n .deb 程序包的维护程序\n --linux-menu-group \n 此应用程序所在的菜单组\n --linux-package-deps \n 应用程序所需的程序包或功能\n --linux-rpm-license-type \n 许可证的类型(RPM .spec 的 "License: ")\n --linux-app-release \n RPM .spec 文件的发行版值或 \n DEB 控制文件的 Debian 修订版值\n --linux-app-category \n RPM .spec 文件的组值或 \n DEB 控制文件的节值\n --linux-shortcut\n 为应用程序创建快捷方式。\n -MSG_Help_mac_linux_install_dir=应用程序安装目录的绝对路径\n -MSG_Help_default_install_dir=OS X 或 Linux 上应用程序安装目录的绝对路径。\n Windows 上应用程序安装位置的相对子路径\n (如 "Program Files" 或 "AppData")。\n -MSG_Help_no_args=用法:jpackage \n使用 jpackage --help(或 -h)可获取可能选项的列表 -MSG_Help_default_app_image=用来构建可安装程序包的\n 预定义应用程序映像的位置\n -MSG_Help_mac_app_image=用来构建可安装程序包的\n 或对预定义应用程序映像进行签名的\n 预定义应用程序映像的位置\n -MSG_Help_mac_sign_sample_usage=\ 对预定义应用程序映像进行签名:\n jpackage --type app-image --app-image \\\n --mac-sign [...]\n 注:此模式下允许的其他选项只有:\n 一组其他 mac 签名选项和 --verbose\n diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties index c61e60fe4ec..6e5de3d9729 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -28,14 +28,14 @@ param.copyright.default=Copyright (C) {0,date,YYYY} param.vendor.default=Unknown bundle-type.win-app=Windows Application Image -bundle-type.win-exe=EXE Installer Package -bundle-type.win-msi=MSI Installer Package +bundle-type.win-exe=Windows EXE Installer +bundle-type.win-msi=Windows MSI Installer bundle-type.mac-app=Mac Application Image bundle-type.mac-dmg=Mac DMG Package bundle-type.mac-pkg=Mac PKG Package bundle-type.linux-app=Linux Application Image -bundle-type.linux-deb=DEB Bundle -bundle-type.linux-rpm=RPM Bundle +bundle-type.linux-deb=Linux DEB Package +bundle-type.linux-rpm=Linux RPM Package resource.post-app-image-script=script to run after application image is populated @@ -43,13 +43,23 @@ message.using-default-resource=Using default package resource {0} {1} (add {2} t message.no-default-resource=No default package resource {0} (add {1} to the resource-dir to customize). message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}). message.using-custom-resource=Using custom package resource {0} (loaded from {1}). -message.creating-app-bundle=Creating app package: {0} in {1} + +message.create-package=Building output package file... +message.create-app-image=Building output application image directory... +message.package-created=Succeeded in building output package file +message.app-image-created=Succeeded in building output application image directory + message.debug-working-directory=Kept working directory for debug: {0} -message.bundle-created=Succeeded in building {0} package + message.module-version=Using version "{0}" from module "{1}" as application version -message.error-header={0} +message.error-header=Error: {0} message.advice-header=Advice to fix: {0} +message.failed-command-output-header=Command output: + +error.command-failed-unexpected-output=Unexpected output from executing the command {0} +error.command-failed-unexpected-exit-code=Unexpected exit code {0} from executing the command {1} +error.command-failed-timed-out=Timed-out command {0} error.version-string-empty=Version may not be empty string error.version-string-zero-length-component=Version [{0}] contains a zero length component @@ -75,13 +85,16 @@ error.parameter-not-directory=The value "{0}" provided for parameter {1} is not error.parameter-not-empty-directory=The value "{0}" provided for parameter {1} is not an empty directory or non existent path error.parameter-not-url=The value "{0}" provided for parameter {1} is not a valid URL error.parameter-not-launcher-shortcut-dir=The value "{0}" provided for parameter {1} is not a valid shortcut startup directory +error.parameter-not-mac-bundle=The value "{0}" provided for parameter {1} is not a valid macOS bundle error.path-parameter-ioexception=I/O error accessing path value "{0}" of parameter {1} error.parameter-add-launcher-malformed=The value "{0}" provided for parameter {1} does not match the pattern = error.parameter-add-launcher-not-file=The value of path to a property file "{0}" provided for additional launcher "{1}" is not a valid file path error.properties-parameter-not-path=The value "{0}" provided for property "{1}" in "{2}" file is not a valid path error.properties-parameter-not-file=The value "{0}" provided for property "{1}" in "{2}" file is not a file +error.properties-parameter-not-directory=The value "{0}" provided for property "{1}" in "{2}" file is not a directory error.properties-parameter-not-launcher-shortcut-dir=The value "{0}" provided for property "{1}" in "{2}" file is not a valid shortcut startup directory +error.no-extensions-for-file-association=No extensions were specified for File Association number {0} error.no-content-types-for-file-association=No MIME types were specified for File Association number {0} error.no-content-types-for-file-association.advice=Specify MIME type for File Association number {0} error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0} @@ -96,7 +109,8 @@ error.tool-not-found.advice=Please install "{0}" error.tool-old-version=Can not find "{0}" {1} or newer error.tool-old-version.advice=Please install "{0}" {1} or newer -error.jlink.failed=jlink failed with: {0} +error.output-bundle-cannot-be-overwritten=Output package file "{0}" exists and can not be removed. + error.blocked.option=jlink option [{0}] is not permitted in --jlink-options error.no.name=Name not specified with --name and cannot infer one from app-image error.no.name.advice=Specify name with --name diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties index 2426b97c7e2..7816f8ee71a 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties @@ -24,25 +24,32 @@ # # -jpackage.description=Eigenständige Java-Anwendung verpacken - param.copyright.default=Copyright (C) {0,date,YYYY} -param.description.default=Kein Wert param.vendor.default=Unbekannt +bundle-type.win-app=Windows-Anwendungsimage +bundle-type.win-exe=EXE-Installationsprogrammpackage +bundle-type.win-msi=MSI-Installationsprogrammpackage +bundle-type.mac-app=Mac-Anwendungsimage +bundle-type.mac-dmg=Mac-DMG-Package +bundle-type.mac-pkg=Mac-PKG-Package +bundle-type.linux-app=Linux-Anwendungsimage +bundle-type.linux-deb=DEB-Bundle +bundle-type.linux-rpm=RPM-Bundle + resource.post-app-image-script=Auszuführendes Skript nach dem Auffüllen des Anwendungsimages message.using-default-resource=Standardpackageressource {0} {1} wird verwendet (durch Hinzufügen von {2} zu resource-dir ist eine Anpassung möglich). -message.no-default-resource=Keine Standardpackageressource {0} {1} (durch Hinzufügen von {2} zu resource-dir ist eine Anpassung möglich). +message.no-default-resource=Keine Standardpackageressource {0} (durch Hinzufügen von {1} zu resource-dir ist eine Anpassung möglich). message.using-custom-resource-from-file=Benutzerdefinierte Packageressource {0} wird verwendet (aus Datei {1} geladen). message.using-custom-resource=Benutzerdefinierte Packageressource {0} wird verwendet (aus {1} geladen). message.creating-app-bundle=Anwendungspackage {0} wird in {1} erstellt -message.runtime-image-dir-does-not-exist=Angegebenes Laufzeitimageverzeichnis {0}: {1} ist nicht vorhanden -message.resource-dir-does-not-exist=Angegebenes Ressourcenverzeichnis {0}: {1} ist nicht vorhanden message.debug-working-directory=Arbeitsverzeichnis für Debug beibehalten: {0} message.bundle-created={0}-Package wurde erfolgreich erstellt message.module-version=Version "{0}" aus Modul "{1}" wird als Anwendungsversion verwendet -message.module-class=Klasse "{0}" aus Modul "{1}" wird als Anwendungshauptklasse verwendet + +message.error-header={0} +message.advice-header=Empfehlung zur Behebung: {0} error.version-string-empty=Version darf keine leere Zeichenfolge sein error.version-string-zero-length-component=Version [{0}] enthält eine Komponente mit Nulllänge @@ -50,64 +57,69 @@ error.version-string-invalid-component=Version [{0}] enthält ungültige Kompone error.cannot-create-output-dir=Zielverzeichnis {0} kann nicht erstellt werden error.cannot-write-to-output-dir=Zielverzeichnis {0} ist schreibgeschützt -error.root-exists=Fehler: Anwendungszielverzeichnis {0} ist bereits vorhanden +error.root-exists=Anwendungszielverzeichnis {0} ist bereits vorhanden error.no-main-class-with-main-jar=Es wurde keine Hauptklasse angegeben oder in der JAR-Datei {0} gefunden error.no-main-class-with-main-jar.advice=Geben Sie eine Hauptklasse an, oder stellen Sie sicher, dass die JAR-Datei {0} eine Hauptklasse im Manifest angibt -error.no-main-class=Es wurde keine Hauptklasse angegeben oder in den angegebenen Anwendungsressourcen gefunden -error.no-main-class.advice=Geben Sie eine Anwendungsklasse an, oder stellen Sie sicher, dass die appResources eine JAR-Datei mit einer Anwendungsklasse im Manifest enthalten error.main-jar-does-not-exist=Die konfigurierte Haupt-JAR-Datei ist im Eingabeverzeichnis nicht vorhanden {0} error.main-jar-does-not-exist.advice=Die Haupt-JAR-Datei muss relativ zum Eingabeverzeichnis (nicht als absoluter Pfad) angegeben werden und muss in diesem Verzeichnis vorhanden sein error.no-module-in-path="{0}-Modul im Modulpfad nicht gefunden" -error.not-path-parameter="Ungültiger Wert für Parameter {0}: {1}" error.no-input-parameter="--input-Parameter für nicht modulare Anwendung fehlt" +error.non-option-arguments={0} Argumente, die keine Option sind, in der Befehlszeile gefunden. Argumente, die keine Option sind, sind nicht zulässig +error.undefined-default-bundling-operation=Standard-Bundling-Vorgang ist nicht definiert +error.undefined-default-bundling-operation.advice=Parameter {0} zur Befehlszeile hinzufügen +error.parameter-not-uuid=Der für Parameter {1} angegebene Wert "{0}" ist keine gültige UUID +error.parameter-not-path=Der für Parameter {1} angegebene Wert "{0}" ist kein gültiger Pfad +error.parameter-not-file=Der für Parameter {1} angegebene Wert "{0}" ist keine Datei +error.parameter-not-directory=Der für Parameter {1} angegebene Wert "{0}" ist kein Verzeichnis +error.parameter-not-empty-directory=Der für Parameter {1} angegebene Wert "{0}" ist kein leeres Verzeichnis oder kein vorhandener Pfad +error.parameter-not-url=Der für Parameter {1} angegebene Wert "{0}" ist keine gültige URL +error.parameter-not-launcher-shortcut-dir=Der für Parameter {1} angegebene Wert "{0}" ist kein gültiges Verknüpfungsstartverzeichnis +error.path-parameter-ioexception=I/O-Fehler beim Zugriff auf Pfadwert "{0}" von Parameter {1} +error.parameter-add-launcher-malformed=Der für Parameter {1} angegebene Wert "{0}" stimmt nicht mit dem Muster = überein +error.parameter-add-launcher-not-file=Der Wert des Pfades zu einer Eigenschaftendatei "{0}", der für den zusätzlichen Launcher "{1}" bereitgestellt wird, ist kein gültiger Dateipfad +error.properties-parameter-not-path=Der für Eigenschaft "{1}" in Datei "{2}" angegebene Wert "{0}" ist kein gültiger Pfad +error.properties-parameter-not-file=Der für Eigenschaft "{1}" in Datei "{2}" angegebene Wert "{0}" ist keine Datei +error.properties-parameter-not-launcher-shortcut-dir=Der für Eigenschaft "{1}" in Datei "{2}" angegebene Wert "{0}" ist kein gültiges Verknüpfungsstartverzeichnis + error.no-content-types-for-file-association=Für Dateiverknüpfungsnummer {0} wurden keine MIME-Typen angegeben error.no-content-types-for-file-association.advice=Geben Sie einen MIME-Typ für Dateiverknüpfungsnummer {0} an error.too-many-content-types-for-file-association=Für Dateiverknüpfungsnummer {0} wurde mehr als ein MIME-Typ angegeben error.too-many-content-types-for-file-association.advice=Geben Sie nur einen MIME-Typ für Dateiverknüpfungsnummer {0} an -error.tool-not-found={0} kann nicht gefunden werden. Grund: {1} -error.tool-not-found.advice=Installieren Sie {0} -error.tool-old-version={0} {1} oder eine neuere Version kann nicht gefunden werden -error.tool-old-version.advice=Installieren Sie {0} {1} oder eine neuere Version +error.launcher-duplicate-name=Mehrere Launcher haben denselben Namen "{0}". Launcher müssen eindeutige Namen haben + +error.tool-error=Validieren von "{0}" nicht möglich. Grund: {1} +error.tool-not-executable="{0}" ist nicht ausführbar +error.tool-not-found="{0}" kann nicht gefunden werden +error.tool-not-found.advice=Installieren Sie "{0}" +error.tool-old-version="{0}" {1} oder eine neuere Version kann nicht gefunden werden +error.tool-old-version.advice=Installieren Sie "{0}" {1} oder eine neuere Version + error.jlink.failed=jlink nicht erfolgreich mit: {0} error.blocked.option=jlink-Option [{0}] ist in --jlink-options nicht zulässig error.no.name=Name nicht mit --name angegeben. Es kann auch kein Name aus app-image abgeleitet werden error.no.name.advice=Geben Sie den Namen mit --name an -warning.no.jdk.modules.found=Warnung: Keine JDK-Module gefunden - -error.foreign-app-image=Fehler : Fehlende .jpackage.xml-Datei in app-image-Verzeichnis "{0}" -error.invalid-app-image=Fehler: app-image-Verzeichnis "{0}" wurde von einer anderen jpackage-Version generiert, oder Datei "{1}" ist nicht wohlgeformt +error.missing-app-image-file=Datei "{0}" fehlt im vordefinierten Anwendungsimage "{1}" +error.invalid-app-image-file=Datei "{0}" im vordefinierten Anwendungsimage "{1}" ist beschädigt oder wurde von einer anderen Version von jpackage erstellt +error.malformed-app-image-file=Datei "{0}" im vordefinierten Anwendungsimage "{1}" enthält nicht wohlgeformte XML-Daten +error.reading-app-image-file=Datei "{0}" konnte im vordefinierten Anwendungsimage "{1}" nicht gelesen werden error.invalid-install-dir=Ungültiges Installationsverzeichnis "{0}" -MSG_BundlerFailed=Fehler: Bundler "{1}" ({0}) konnte kein Package generieren -MSG_BundlerConfigException=Bundler {0} aufgrund eines Konfigurationsproblems übersprungen: {1} \nEmpfehlung zur Behebung: {2} -MSG_BundlerConfigExceptionNoAdvice=Bundler {0} aufgrund eines Konfigurationsproblems übersprungen: {1} -MSG_BundlerRuntimeException=Bundler {0} nicht erfolgreich. Grund: {1} +ERR_NoMainClass=Hauptanwendungsklasse fehlt +ERR_UnsupportedOption=Option [{0}] ist auf dieser Plattform ungültig +ERR_InvalidTypeOption=Option [{0}] ist nicht gültig mit Typ [{1}] +ERR_NoInstallerEntryPoint=Option [{0}] ist nicht gültig ohne Einstiegspunktoption --module oder --main-jar +ERR_MutuallyExclusiveOptions=Sich gegenseitig ausschließende Optionen: [{0}] und [{1}] +ERR_InvalidOptionWithAppImageSigning=Option [{0}] ist nicht gültig beim Signieren eines Anwendungsimages -ERR_NoMainClass=Fehler: Hauptanwendungsklasse fehlt -ERR_UnsupportedOption=Fehler: Option [{0}] ist auf dieser Plattform ungültig -ERR_InvalidTypeOption=Fehler: Option [{0}] ist nicht gültig mit Typ [{1}] -ERR_NoInstallerEntryPoint=Fehler: Option [{0}] ist nicht gültig ohne Einstiegspunktoption --module oder --main-jar -ERR_MutuallyExclusiveOptions=Fehler: Optionen [{0}] und [{1}] schließen sich gegenseitig aus -ERR_InvalidOptionWithAppImageSigning=Fehler: Option [{0}] ist nicht gültig beim Signieren eines Anwendungsimages - -ERR_MissingArgument=Fehler: Fehlendes Argument: {0} -ERR_MissingRequiredArgument=Fehler: Für das Argument {0} ist mindestens eines der folgenden Argumente erforderlich: [{1}] -ERR_AppImageNotExist=Fehler: Anwendungsimageverzeichnis "{0}" ist nicht vorhanden -ERR_NoAddLauncherName=Fehler: Für Option --add-launcher müssen ein Name und ein Dateipfad angegeben werden (--add-launcher =) -ERR_NoUniqueName=Fehler: Für --add-launcher = ist ein eindeutiger Name erforderlich -ERR_InvalidAppName=Fehler: Ungültiger Anwendungsname: {0} -ERR_InvalidSLName=Fehler: Ungültiger Name für hinzuzufügenden Launcher: {0} -ERR_IconFileNotExit=Fehler: Die angegebene Symboldatei [{0}] ist nicht vorhanden -ERR_LicenseFileNotExit=Fehler: Die angegebene Lizenzdatei ist nicht vorhanden -ERR_BuildRootInvalid=Fehler: Das temporäre Verzeichnis ({0}) darf nicht vorhanden sein oder muss leer sein -ERR_InvalidOption=Fehler: Ungültige Option: [{0}] -ERR_InvalidInstallerType=Fehler: Ungültiger oder nicht unterstützter Typ: [{0}] -ERR_BothMainJarAndModule=Fehler: Die Optionen --main-jar und --module dürfen nicht beide vorhanden sein -ERR_NoEntryPoint=Fehler: Für das Erstellen des Anwendungsimages muss entweder die Option --main-jar oder die Option --module angegeben werden -ERR_CannotParseOptions=Fehler: Option @filename wird verarbeitet: {0} -ERR_MissingJLinkOptMacAppStore=Fehler: Argument "--mac-app-store" erfordert eine {0}-Option für Argument "--jlink-options" -ERR_MacAppStoreRuntimeBinExists=Fehler: Laufzeitimage "{0}" darf nicht den Ordner "bin" enthalten. Verwenden Sie die jlink-Option "--strip-native-commands" beim Generieren des Laufzeitimages mit dem Argument "--mac-app-store". +ERR_MissingArgument2=Fehlendes Argument: {0} oder {1} +ERR_InvalidAppName=Ungültiger Anwendungsname: {0} +ERR_InvalidSLName=Ungültiger Name für hinzuzufügenden Launcher: {0} +ERR_InvalidOption=Ungültige Option: [{0}] +ERR_InvalidInstallerType=Ungültiger oder nicht unterstützter Typ: [{0}] +ERR_NoEntryPoint=Für das Erstellen des Anwendungsimages muss entweder die Option --main-jar oder die Option --module angegeben werden +ERR_CannotParseOptions=Option @filename wird verarbeitet: {0} +ERR_MissingJLinkOptMacAppStore=Argument "--mac-app-store" erfordert eine {0}-Option für Argument "--jlink-options" diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties index e0faf346973..5db5ead7577 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties @@ -24,25 +24,32 @@ # # -jpackage.description=自己完結型Javaアプリケーションをパッケージ化します - param.copyright.default=Copyright (C) {0,date,YYYY} -param.description.default=なし param.vendor.default=不明 +bundle-type.win-app=Windowsアプリケーション・イメージ +bundle-type.win-exe=EXEインストーラ・パッケージ +bundle-type.win-msi=MSIインストーラ・パッケージ +bundle-type.mac-app=Macアプリケーション・イメージ +bundle-type.mac-dmg=Mac DMGパッケージ +bundle-type.mac-pkg=Mac PKGパッケージ +bundle-type.linux-app=Linuxアプリケーション・イメージ +bundle-type.linux-deb=DEBバンドル +bundle-type.linux-rpm=RPMバンドル + resource.post-app-image-script=アプリケーション・イメージを移入した後に実行するスクリプト message.using-default-resource=デフォルトのパッケージ・リソース{0} {1}の使用({2}をresource-dirに追加してカスタマイズ)。 -message.no-default-resource=デフォルトのパッケージ・リソース{0} {1}なし({2}をresource-dirに追加してカスタマイズ)。 +message.no-default-resource=デフォルトのパッケージ・リソース{0}なし({1}をresource-dirに追加してカスタマイズ)。 message.using-custom-resource-from-file=カスタム・パッケージ・リソース{0}の使用(ファイル{1}からロード済) message.using-custom-resource=カスタム・パッケージ・リソース{0}の使用({1}からロード済) message.creating-app-bundle=アプリケーション・パッケージを作成しています: {1}内の{0} -message.runtime-image-dir-does-not-exist=指定されたランタイム・イメージ・ディレクトリ{0}: {1}は存在しません -message.resource-dir-does-not-exist=指定されたリソース・ディレクトリ{0}: {1}は存在しません message.debug-working-directory=デバッグの作業ディレクトリが保持されました: {0} message.bundle-created={0}パッケージの作成に成功しました message.module-version=モジュール"{1}"のバージョン"{0}"をアプリケーション・バージョンとして使用 -message.module-class=モジュール"{1}"のクラス"{0}"をアプリケーション・メイン・クラスとして使用 + +message.error-header={0} +message.advice-header=修正のアドバイス: {0} error.version-string-empty=バージョンを空の文字列にすることはできません error.version-string-zero-length-component=バージョン[{0}]に長さゼロのコンポーネントが含まれます @@ -50,64 +57,69 @@ error.version-string-invalid-component=バージョン[{0}]に無効なコンポ error.cannot-create-output-dir=宛先ディレクトリ{0}を作成できません。 error.cannot-write-to-output-dir=宛先ディレクトリ{0}は書込み不可です -error.root-exists=エラー: アプリケーションの宛先ディレクトリ{0}はすでに存在します +error.root-exists=アプリケーションの宛先ディレクトリ{0}はすでに存在します error.no-main-class-with-main-jar=メイン・クラスが指定されていなかったか、jar {0}に見つかりませんでした error.no-main-class-with-main-jar.advice=メイン・クラスを指定するか、jar {0}がマニフェストで指定していることを確認してください -error.no-main-class=メイン・クラスが指定されていなかったか、指定されたアプリケーション・リソースに見つかりませんでした -error.no-main-class.advice=アプリケーション・クラスを指定するか、マニフェストでappResourcesにアプリケーション・クラスを含むjarがあることを確認してください error.main-jar-does-not-exist=入力ディレクトリで、構成されたメインjarが{0}に存在しません error.main-jar-does-not-exist.advice=入力ディレクトリに対して相対的に(絶対パスではない)メインjarを指定する必要があり、そのディレクトリ内に存在する必要があります error.no-module-in-path="モジュール・パスに{0}モジュールが見つかりませんでした" -error.not-path-parameter="{0}パラメータの無効な値: {1}" error.no-input-parameter="非モジュラ・アプリケーションに--inputパラメータがありません" +error.non-option-arguments=コマンドラインで{0}個の非オプション引数が見つかりました。非オプション引数は使用できません +error.undefined-default-bundling-operation=デフォルトのバンドル操作は未定義です +error.undefined-default-bundling-operation.advice=コマンドラインに{0}パラメータを追加します +error.parameter-not-uuid=パラメータ{1}に指定された値"{0}"は有効なUUIDではありません +error.parameter-not-path=パラメータ{1}に指定された値"{0}"は有効なパスではありません +error.parameter-not-file=パラメータ{1}に指定された値"{0}"はファイルではありません +error.parameter-not-directory=パラメータ{1}に指定された値"{0}"はディレクトリではありません +error.parameter-not-empty-directory=パラメータ{1}に指定された値"{0}"が空のディレクトリでないか、存在しないパスです +error.parameter-not-url=パラメータ{1}に指定された値"{0}"は有効なURLではありません +error.parameter-not-launcher-shortcut-dir=パラメータ{1}に指定された値"{0}"は、有効なショートカット起動ディレクトリではありません +error.path-parameter-ioexception=パラメータ{1}のパス値"{0}"へのアクセス中にI/Oエラーが発生しました +error.parameter-add-launcher-malformed=パラメータ{1}に指定された値"{0}"がパターン=と一致しません +error.parameter-add-launcher-not-file=追加のランチャ"{1}"に指定されたプロパティ・ファイル"{0}"へのパスの値は有効なファイル・パスではありません +error.properties-parameter-not-path="{2}"ファイルのプロパティ"{1}"に指定された値"{0}"は有効なパスではありません +error.properties-parameter-not-file="{2}"ファイルのプロパティ"{1}"に指定された値"{0}"はファイルではありません +error.properties-parameter-not-launcher-shortcut-dir="{2}"ファイルのプロパティ"{1}"に指定された値"{0}"は、有効なショートカット起動ディレクトリではありません + error.no-content-types-for-file-association=ファイル・アソシエーション番号{0}にMIMEタイプが指定されませんでした error.no-content-types-for-file-association.advice=ファイル・アソシエーション番号{0}にMIMEタイプを指定してください error.too-many-content-types-for-file-association=ファイル・アソシエーション番号{0}に複数のMIMEタイプが指定されました error.too-many-content-types-for-file-association.advice=ファイル・アソシエーション番号{0}にMIMEタイプを1つのみ指定してください -error.tool-not-found={0}が見つかりません。理由: {1} -error.tool-not-found.advice={0}をインストールしてください -error.tool-old-version={0} {1}以降が見つかりません -error.tool-old-version.advice={0} {1}以降をインストールしてください +error.launcher-duplicate-name=複数のランチャに同じ名前"{0}"が付いています。ランチャには一意の名前が必要です + +error.tool-error="{0}"を検証できません。理由: {1} +error.tool-not-executable="{0}"は実行可能ではありません +error.tool-not-found="{0}"が見つかりません +error.tool-not-found.advice="{0}"をインストールしてください +error.tool-old-version="{0}" {1}以降が見つかりません +error.tool-old-version.advice="{0}" {1}以降をインストールしてください + error.jlink.failed=jlinkが次で失敗しました: {0} error.blocked.option=jlinkオプション[{0}]は--jlink-optionsでは許可されません error.no.name=名前が--nameで指定されておらず、app-imageから推論できません error.no.name.advice=--nameで名前を指定します -warning.no.jdk.modules.found=警告: JDKモジュールが見つかりません - -error.foreign-app-image=エラー: app-imageディレクトリ"{0}"に.jpackage.xmlファイルがありません -error.invalid-app-image=エラー: app-imageディレクトリ"{0}"は、別のjpackageバージョンまたは不正な"{1}"ファイルで生成されました +error.missing-app-image-file=事前定義済アプリケーション・イメージ"{1}"に"{0}"ファイルがありません +error.invalid-app-image-file=事前定義済アプリケーション・イメージ"{1}"の"{0}"ファイルが破損しているか、別のバージョンのjpackageによって作成されました +error.malformed-app-image-file=事前定義済アプリケーション・イメージ"{1}"の"{0}"ファイルに不正なXMLデータが含まれています +error.reading-app-image-file=事前定義済アプリケーション・イメージ"{1}"の"{0}"ファイルの読取りに失敗しました error.invalid-install-dir=無効なインストール・ディレクトリ"{0}" -MSG_BundlerFailed=エラー: バンドラ"{1}" ({0})がパッケージの生成に失敗しました -MSG_BundlerConfigException=構成の問題のため、バンドラ{0}がスキップされました: {1} \n次の修正を行ってください: {2} -MSG_BundlerConfigExceptionNoAdvice=構成の問題のため、バンドラ{0}がスキップされました: {1} -MSG_BundlerRuntimeException={1}のため、バンドラ{0}が失敗しました +ERR_NoMainClass=メイン・アプリケーション・クラスがありません +ERR_UnsupportedOption=オプション[{0}]は、このプラットフォームでは無効です +ERR_InvalidTypeOption=オプション[{0}]は、タイプ[{1}]では無効です +ERR_NoInstallerEntryPoint=オプション[{0}]は、--moduleまたは--main-jarエントリ・ポイント・オプションなしでは無効です +ERR_MutuallyExclusiveOptions=相互排他的なオプション[{0}]と[{1}] +ERR_InvalidOptionWithAppImageSigning=アプリケーション・イメージへの署名時にオプション[{0}]が有効ではありません -ERR_NoMainClass=エラー: メイン・アプリケーション・クラスがありません -ERR_UnsupportedOption=エラー: オプション[{0}]は、このプラットフォームでは無効です -ERR_InvalidTypeOption=エラー: オプション[{0}]は、タイプ[{1}]では無効です -ERR_NoInstallerEntryPoint=エラー: オプション[{0}]は、--moduleまたは--main-jarエントリ・ポイント・オプションなしでは無効です -ERR_MutuallyExclusiveOptions=エラー: 相互排他的なオプション[{0}]と[{1}] -ERR_InvalidOptionWithAppImageSigning=エラー: アプリケーション・イメージへの署名時にオプション[{0}]が有効ではありません - -ERR_MissingArgument=エラー: 引数がありません: {0} -ERR_MissingRequiredArgument=エラー: {0}引数には少なくとも1つの[{1}]引数が必要です -ERR_AppImageNotExist=エラー: アプリケーション・イメージ・ディレクトリ"{0}"は存在しません -ERR_NoAddLauncherName=エラー: --add-launcherオプションには名前およびファイル・パスが必要です(--add-launcher =) -ERR_NoUniqueName=エラー: --add-launcher =には一意の名前が必要です -ERR_InvalidAppName=エラー: 無効なアプリケーション名: {0} -ERR_InvalidSLName=エラー: 無効な追加ランチャ名: {0} -ERR_IconFileNotExit=エラー: 指定されたアイコン・ファイル[{0}]は存在しません -ERR_LicenseFileNotExit=エラー: 指定されたライセンス・ファイルは存在しません -ERR_BuildRootInvalid=エラー: 一時({0})は存在しないか、空のディレクトリである必要があります -ERR_InvalidOption=エラー: 無効なオプション: [{0}] -ERR_InvalidInstallerType=エラー: 無効またはサポートされていないタイプ: [{0}] -ERR_BothMainJarAndModule=エラー: --main-jarオプションと--moduleオプションの両方を指定することはできません -ERR_NoEntryPoint=エラー: アプリケーション・イメージの作成には--main-jarまたは--moduleオプションが必要です -ERR_CannotParseOptions=エラー: @filenameオプションの処理: {0} -ERR_MissingJLinkOptMacAppStore=エラー: --mac-app-store引数では、--jlink-options引数に{0}オプションが必要です -ERR_MacAppStoreRuntimeBinExists=エラー: ランタイム・イメージ"{0}"に"bin"フォルダを含めることはできません。--mac-app-store引数で使用されるランタイム・イメージを生成する際に、--strip-native-commands jlinkオプションを使用します。 +ERR_MissingArgument2=引数がありません: {0}または{1} +ERR_InvalidAppName=無効なアプリケーション名: {0} +ERR_InvalidSLName=無効な追加ランチャ名: {0} +ERR_InvalidOption=無効なオプション: [{0}] +ERR_InvalidInstallerType=無効またはサポートされていないタイプ: [{0}] +ERR_NoEntryPoint=アプリケーション・イメージの作成には--main-jarまたは--moduleオプションが必要です +ERR_CannotParseOptions=@filenameオプションの処理: {0} +ERR_MissingJLinkOptMacAppStore=--mac-app-store引数では、--jlink-options引数に{0}オプションが必要です diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties index 0236ef34077..23540af7db2 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties @@ -24,25 +24,32 @@ # # -jpackage.description=打包自包含 Java 应用程序 - param.copyright.default=版权所有 (C) {0,date,YYYY} -param.description.default=无 param.vendor.default=未知 +bundle-type.win-app=Windows 应用程序映像 +bundle-type.win-exe=EXE 安装程序包 +bundle-type.win-msi=MSI 安装程序包 +bundle-type.mac-app=Mac 应用程序映像 +bundle-type.mac-dmg=Mac DMG 程序包 +bundle-type.mac-pkg=Mac PKG 程序包 +bundle-type.linux-app=Linux 应用程序映像 +bundle-type.linux-deb=DEB 包 +bundle-type.linux-rpm=RPM 包 + resource.post-app-image-script=要在填充应用程序映像之后运行的脚本 message.using-default-resource=使用默认程序包资源 {0} {1}(将 {2} 添加到 resource-dir 中以进行定制)。 -message.no-default-resource=无默认程序包资源 {0} {1}(将 {2} 添加到 resource-dir 中以进行定制)。 +message.no-default-resource=无默认程序包资源 {0}(将 {1} 添加到 resource-dir 中以进行定制)。 message.using-custom-resource-from-file=使用定制程序包资源 {0} (从文件 {1} 加载)。 message.using-custom-resource=使用定制程序包资源 {0} (从 {1} 加载)。 message.creating-app-bundle=正在 {1} 中创建应用程序包 {0} -message.runtime-image-dir-does-not-exist=指定的运行时映像目录 {0}:{1} 不存在 -message.resource-dir-does-not-exist=指定的资源目录 {0}:{1} 不存在 message.debug-working-directory=用于调试的已保留工作目录: {0} message.bundle-created=已成功地构建 {0} 程序包 message.module-version=正在将模块 "{1}" 中的版本 "{0}" 用作应用程序版本 -message.module-class=正在将模块 "{1}" 中的类 "{0}" 用作应用程序主类 + +message.error-header={0} +message.advice-header=修复建议:{0} error.version-string-empty=版本不能为空字符串 error.version-string-zero-length-component=版本 [{0}] 包含长度为零的组件 @@ -50,64 +57,69 @@ error.version-string-invalid-component=版本 [{0}] 包含无效组件 [{1}] error.cannot-create-output-dir=无法创建目标目录 {0} error.cannot-write-to-output-dir=目标目录 {0} 不可写 -error.root-exists=错误:应用程序目标目录 {0} 已存在 +error.root-exists=应用程序目标目录 {0} 已存在 error.no-main-class-with-main-jar=未指定主类,在 jar {0} 中也未找到主类 error.no-main-class-with-main-jar.advice=请指定主类或确保 jar {0} 在清单中指定一个主类。 -error.no-main-class=未指定主类,在提供的应用程序资源中也未找到主类 -error.no-main-class.advice=请指定应用程序类,或者确保 appResources 中有一个 jar 在清单中包含应用程序类。 error.main-jar-does-not-exist=配置的主 jar 在输入目录中不存在 {0} error.main-jar-does-not-exist.advice=必须使用相对于输入目录的路径(不使用绝对路径)指定主 jar ,并且该目录中存在主 jar error.no-module-in-path="无法在模块路径中找到 {0} 模块" -error.not-path-parameter="{0} 参数的值无效:{1}" error.no-input-parameter="非模块化应用程序缺少 --input 参数" +error.non-option-arguments=在命令行上发现 {0} 个非选项参数。不允许使用非选项参数 +error.undefined-default-bundling-operation=未定义默认绑定操作 +error.undefined-default-bundling-operation.advice=将 {0} 参数添加到命令行 +error.parameter-not-uuid=为参数 {1} 提供的值 "{0}" 不是有效的 UUID +error.parameter-not-path=为参数 {1} 提供的值 "{0}" 不是有效路径 +error.parameter-not-file=为参数 {1} 提供的值 "{0}" 不是文件 +error.parameter-not-directory=为参数 {1} 提供的值 "{0}" 不是目录 +error.parameter-not-empty-directory=为参数 {1} 提供的值 "{0}" 不是空目录或是不存在的路径 +error.parameter-not-url=为参数 {1} 提供的值 "{0}" 不是有效的 URL +error.parameter-not-launcher-shortcut-dir=为参数 {1} 提供的值 "{0}" 不是有效的快捷方式启动目录 +error.path-parameter-ioexception=访问参数 {1} 的路径值 "{0}" 时出现 I/O 错误 +error.parameter-add-launcher-malformed=为参数 {1} 提供的值 "{0}" 与模式 = 不匹配 +error.parameter-add-launcher-not-file=为其他启动程序 "{1}" 提供的属性文件 "{0}" 的路径值不是有效的文件路径 +error.properties-parameter-not-path=为 "{2}" 文件中的属性 "{1}" 提供的值 "{0}" 不是有效路径 +error.properties-parameter-not-file=为 "{2}" 文件中的属性 "{1}" 提供的值 "{0}" 不是文件 +error.properties-parameter-not-launcher-shortcut-dir=为 "{2}" 文件中的属性 "{1}" 提供的值 "{0}" 不是有效的快捷方式启动目录 + error.no-content-types-for-file-association=没有为文件关联号{0}指定 MIME 类型 error.no-content-types-for-file-association.advice=为文件关联号 {0} 指定 MIME 类型 error.too-many-content-types-for-file-association=为文件关联号{0}指定了多个 MIME 类型 error.too-many-content-types-for-file-association.advice=仅为文件关联号 {0} 指定一个 MIME 类型 -error.tool-not-found=找不到 {0}。原因:{1} -error.tool-not-found.advice=请安装 {0} -error.tool-old-version=找不到 {0} {1}或更新版本 -error.tool-old-version.advice=请安装 {0} {1}或更新版本 +error.launcher-duplicate-name=多个启动程序具有相同的名称 "{0}"。启动程序应具有唯一名称 + +error.tool-error=无法验证 "{0}"。原因:{1} +error.tool-not-executable="{0}" 不可执行 +error.tool-not-found=找不到 "{0}" +error.tool-not-found.advice=请安装 "{0}" +error.tool-old-version=找不到 "{0}" {1} 或更新版本 +error.tool-old-version.advice=请安装 "{0}" {1} 或更新版本 + error.jlink.failed=jlink 失败,出现 {0} error.blocked.option=不允许在 --jlink-options 中使用 jlink 选项 [{0}] error.no.name=未使用 --name 指定名称,无法从 app-image 推断名称 error.no.name.advice=使用 --name 指定名称 -warning.no.jdk.modules.found=警告: 未找到 JDK 模块 - -error.foreign-app-image=错误:app-image 目录 "{0}" 中缺少 .jpackage.xml 文件 -error.invalid-app-image=错误:另一个 jpackage 版本或格式错误的 "{1}" 文件生成了 app-image 目录 "{0}" +error.missing-app-image-file=预定义的应用程序映像 "{1}" 中缺少 "{0}" 文件 +error.invalid-app-image-file=预定义的应用程序映像 "{1}" 中的 "{0}" 文件已损坏或由其他版本的 jpackage 创建 +error.malformed-app-image-file=预定义的应用程序映像 "{1}" 中的 "{0}" 文件包含格式错误的 XML 数据 +error.reading-app-image-file=无法在预定义的应用程序映像 "{1}" 中读取 "{0}" 文件 error.invalid-install-dir=安装目录 "{0}" 无效 -MSG_BundlerFailed=错误:打包程序 "{1}" ({0}) 无法生成程序包 -MSG_BundlerConfigException=由于配置问题, 跳过了打包程序{0}: {1} \n修复建议: {2} -MSG_BundlerConfigExceptionNoAdvice=由于配置问题, 跳过了打包程序{0}: {1} -MSG_BundlerRuntimeException=由于{1}, 打包程序{0}失败 +ERR_NoMainClass=缺少主应用程序类 +ERR_UnsupportedOption=选项 [{0}] 在此平台上无效 +ERR_InvalidTypeOption=选项 [{0}] 对于类型 [{1}] 无效 +ERR_NoInstallerEntryPoint=在没有 --module 或 --main-jar 入口点选项时,选项 [{0}] 无效 +ERR_MutuallyExclusiveOptions=选项 [{0}] 和 [{1}] 相互排斥 +ERR_InvalidOptionWithAppImageSigning=对应用程序映像进行签名时,选项 [{0}] 无效 -ERR_NoMainClass=错误:缺少主应用程序类 -ERR_UnsupportedOption=错误:选项 [{0}] 在此平台上无效 -ERR_InvalidTypeOption=错误:选项 [{0}] 对于类型 [{1}] 无效 -ERR_NoInstallerEntryPoint=错误:在没有 --module 或 --main-jar 入口点选项时,选项 [{0}] 无效 -ERR_MutuallyExclusiveOptions=错误:选项 [{0}] 和 [{1}] 相互排斥 -ERR_InvalidOptionWithAppImageSigning=错误:对应用程序映像签名时,选项 [{0}] 无效 - -ERR_MissingArgument=错误: 缺少参数: {0} -ERR_MissingRequiredArgument=错误:{0} 参数至少需要 [{1}] 参数之一 -ERR_AppImageNotExist=错误:应用程序映像目录 "{0}" 不存在 -ERR_NoAddLauncherName=错误:--add-launcher 选项需要一个名称和一个文件路径 (--add-launcher =) -ERR_NoUniqueName=错误:--add-launcher = 需要一个唯一的名称 -ERR_InvalidAppName=错误:应用程序名称 {0} 无效 -ERR_InvalidSLName=错误:添加启动程序名称 {0} 无效 -ERR_IconFileNotExit=错误:指定的图标文件 [{0}] 不存在 -ERR_LicenseFileNotExit=错误:指定的许可证文件不存在 -ERR_BuildRootInvalid=错误:临时目录 ({0}) 必须是不存在的目录或空白目录 -ERR_InvalidOption=错误:选项 [{0}] 无效 -ERR_InvalidInstallerType=错误:类型 [{0}] 无效或不受支持 -ERR_BothMainJarAndModule=错误:不能同时包含 --main-jar 和 --module 选项 -ERR_NoEntryPoint=错误:创建应用程序映像需要 --main-jar 或 --module 选项 -ERR_CannotParseOptions=错误:正在处理 @filename 选项:{0} -ERR_MissingJLinkOptMacAppStore=错误:对于 --jlink-options 参数,--mac-app-store 参数需要 {0} 选项 -ERR_MacAppStoreRuntimeBinExists=错误:运行时映像 "{0}" 不应包含 "bin" 文件夹。生成与 --mac-app-store 参数一起使用的运行时映像时,使用 --strip-native-commands jlink 选项。 +ERR_MissingArgument2=缺少参数:{0} 或 {1} +ERR_InvalidAppName=应用程序名称 {0} 无效 +ERR_InvalidSLName=添加启动程序名称 {0} 无效 +ERR_InvalidOption=选项 [{0}] 无效 +ERR_InvalidInstallerType=类型 [{0}] 无效或不受支持 +ERR_NoEntryPoint=创建应用程序映像需要 --main-jar 或 --module 选项 +ERR_CannotParseOptions=正在处理 @filename 选项:{0} +ERR_MissingJLinkOptMacAppStore=对于 --jlink-options 参数,--mac-app-store 参数需要 {0} 选项 diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java index 93d317b0128..cb03a1fee8a 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandLineFormat.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandLineFormat.java new file mode 100644 index 00000000000..4d64030876f --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandLineFormat.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.util; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Formats command line arguments. + */ +public final class CommandLineFormat { + + public String format(List cmdline) { + return cmdline.stream().map(enquoter::applyTo).collect(Collectors.joining(" ")); + } + + public static CommandLineFormat platform() { + var format = new CommandLineFormat(); + format.enquoter = Enquoter.identity().setEnquotePredicate(Enquoter.QUOTE_IF_WHITESPACES).setQuoteChar('\''); + return format; + } + + private CommandLineFormat() { + } + + private Enquoter enquoter; + + public static final Function, String> DEFAULT = platform()::format; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java new file mode 100644 index 00000000000..00fbdd71919 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java @@ -0,0 +1,1949 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.util; + +import static java.util.stream.Collectors.joining; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.StringReader; +import java.io.UncheckedIOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntPredicate; +import java.util.function.Predicate; +import java.util.spi.ToolProvider; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingConsumer; +import jdk.jpackage.internal.util.function.ThrowingRunnable; + +/** + * Runs commands and processes their stdout and stderr streams. + *

+ * A command is either a subprocess represented by {@link ProcessBuilder} or a + * tool provided by {@link ToolProvider}. + *

+ * A command is executed synchronously, and the result of its execution is + * stored in a {@link Result} instance which captures the exit code and any + * saved output streams. + *

+ * Depending on the configuration, it can save the entire output stream, only + * the first line, or not save the output at all. Stdout and stderr streams can + * be configured independently. + *

+ * Output streams can be treated as either byte streams or character streams. + * + *

+ * The table below shows how different parameter combinations affect the content + * written to streams returned by {@link #dumpStdout()} and + * {@link #dumpStderr()} for subsequently executed tools, regardless of whether + * their output streams are saved, or for subprocesses when the output streams + * are saved: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
discardStdout(false) and discardStderr(false)discardStdout(false) and discardStderr(true)discardStdout(true) and discardStderr(false)
redirectStderr(true) and dumpOutput(true) + *

+ * dumpStdout(): STDOUT and STDERR interleaved + *

+ * dumpStderr(): unchanged

+ *

+ * dumpStdout(): STDOUT + *

+ * dumpStderr(): unchanged

+ *

+ * dumpStdout(): STDERR; + *

+ * dumpStderr(): unchanged

redirectStderr(false) and dumpOutput(true) + *

+ * dumpStdout(): STDOUT + *

+ * dumpStderr(): STDERR

+ *

+ * dumpStdout(): STDOUT + *

+ * dumpStderr(): unchanged

+ *

+ * dumpStdout(): unchanged + *

+ * dumpStderr(): STDERR

+ * + *

+ * The table below shows how different parameter combinations affect the content + * written to the native file descriptors associated with {@link System#out} and + * {@link System#err} for subsequently executed subprocesses when the output + * streams are not saved: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
discardStdout(false) and discardStderr(false)discardStdout(false) and discardStderr(true)discardStdout(true) and discardStderr(false)
redirectStderr(true) and dumpOutput(true) + *

+ * System.out: STDOUT and STDERR interleaved + *

+ * System.err: unchanged

+ *

+ * System.out: STDOUT + *

+ * System.err: unchanged

+ *

+ * System.out: STDERR; + *

+ * The command's STDERR will be written to the stream referenced by + * {@link #dumpStdout()} rather than to the underlying file descriptor + * associated with the Java process's STDOUT + *

+ * System.err: unchanged

redirectStderr(false) and dumpOutput(true) + *

+ * System.out: STDOUT + *

+ * System.err: STDERR

+ *

+ * System.out: STDOUT + *

+ * System.err: unchanged

+ *

+ * System.out: unchanged + *

+ * System.err: STDERR

+ * + *

+ * The table below shows how different parameter combinations affect the + * properties of {@link Result} objects returned by + * {@link #execute(ProcessBuilder, long)} or + * {@link #execute(ToolProvider, long, String...)} when processing character + * streams: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
saveOutput(true)saveFirstLineOfOutput()
redirectStderr(true) and discardStdout(false) and + * discardStderr(false) + *

+ * content(): STDOUT and STDERR interleaved + *

+ * findStdout(): {@code Optional.empty()} + *

+ * findStderr(): {@code Optional.empty()}

+ *

+ * content(): a single-item list containing the first line of interleaved STDOUT + * and STDERR if the command produced any output; otherwise, an empty list + *

+ * findStdout(): {@code Optional.empty()} + *

+ * findStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(false) and + * discardStderr(false) + *

+ * content(): STDOUT followed by STDERR + *

+ * stdout(): STDOUT + *

+ * stderr(): STDERR

+ *

+ * content(): a list containing at most two items: the first line of STDOUT (if + * the command produced any), followed by the first line of STDERR (if the + * command produced any) + *

+ * stdout(): The first line of STDOUT (if the command produced any); otherwise + * an empty list + *

+ * findStderr(): The first line of STDERR (if the command produced any); + * otherwise an empty list + *

redirectStderr(true) and discardStdout(false) and + * discardStderr(true) + *

+ * content(): STDOUT + *

+ * stdout(): The same as content() + *

+ * findStderr(): {@code Optional.empty()}

+ *

+ * content(): The first line of STDOUT (if the command produced any); otherwise + * an empty list + *

+ * stdout(): The same as content() + *

+ * findStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(false) and + * discardStderr(true) + *

+ * content(): STDOUT + *

+ * stdout(): The same as content() + *

+ * stderr(): an empty list

+ *

+ * content(): The first line of STDOUT (if the command produced any); otherwise + * an empty list + *

+ * stdout(): The same as content() + *

+ * stderr(): an empty list

redirectStderr(true) and discardStdout(true) and + * discardStderr(false) + *

+ * content(): STDERR + *

+ * stdout(): The same as content() + *

+ * findStderr(): {@code Optional.empty()}

+ *

+ * content(): The first line of STDERR (if the command produced any); otherwise + * an empty list + *

+ * stdout(): The same as content() + *

+ * findStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(true) and + * discardStderr(false) + *

+ * content(): STDERR + *

+ * findStdout(): an empty list + *

+ * stderr(): The same as content()

+ *

+ * content(): The first line of STDERR (if the command produced any); otherwise + * an empty list + *

+ * findStdout(): an empty list + *

+ * stderr(): The same as content()

+ *

+ * The table below shows how different parameter combinations affect the + * properties of {@link Result} objects returned by + * {@link #execute(ProcessBuilder, long)} or + * {@link #execute(ToolProvider, long, String...)} when processing byte streams: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
saveOutput(true) or saveFirstLineOfOutput()
redirectStderr(true) and discardStdout(false) and + * discardStderr(false) + *

+ * byteContent(): STDOUT and STDERR interleaved + *

+ * findByteStdout(): {@code Optional.empty()} + *

+ * findByteStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(false) and + * discardStderr(false) + *

+ * byteContent(): STDOUT followed by STDERR + *

+ * byteStdout(): STDOUT + *

+ * byteStderr(): STDERR

redirectStderr(true) and discardStdout(false) and + * discardStderr(true) + *

+ * byteContent(): STDOUT + *

+ * byteStdout(): The same as byteContent() + *

+ * findByteStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(false) and + * discardStderr(true) + *

+ * byteContent(): STDOUT + *

+ * byteStdout(): The same as byteContent() + *

+ * byteStderr(): an empty array

redirectStderr(true) and discardStdout(true) and + * discardStderr(false) + *

+ * byteContent(): STDERR + *

+ * byteStdout(): The same as byteContent() + *

+ * findByteStderr(): {@code Optional.empty()}

redirectStderr(false) and discardStdout(true) and + * discardStderr(false) + *

+ * byteContent(): STDERR + *

+ * findByteStdout(): an empty array + *

+ * byteStderr(): The same as byteContent()

+ */ +public final class CommandOutputControl { + + public CommandOutputControl() { + outputStreamsControl = new OutputStreamsControl(); + } + + private CommandOutputControl(CommandOutputControl other) { + flags = other.flags; + outputStreamsControl = other.outputStreamsControl.copy(); + dumpStdout = other.dumpStdout; + dumpStderr = other.dumpStderr; + charset = other.charset; + processListener = other.processListener; + } + + /** + * Specifies whether the full output produced by commands subsequently executed + * by this object will be saved. + *

+ * If {@code v} is {@code true}, both stdout and stderr streams will be saved; + * otherwise, they will not be saved. + *

+ * This setting is mutually exclusive with {@link #saveFirstLineOfOutput()}. + * + * @param v {@code true} to save the full stdout and stderr streams; + * {@code false} otherwise + * @return this + */ + public CommandOutputControl saveOutput(boolean v) { + return setOutputControl(v, OutputControlOption.SAVE_ALL); + } + + /** + * Returns whether this object will save the complete output of commands + * subsequently executed. + * + * @return {@code true} if this object will save the full output of commands it + * executes subsequently; {@code false} otherwise + */ + public boolean isSaveOutput() { + return outputStreamsControl.stdout().saveAll(); + } + + /** + * Specifies whether the first line of the output, combined from the stdout and + * stderr streams of commands subsequently executed by this object, will be + * saved. + *

+ * This setting is mutually exclusive with {@link #saveOutput(boolean)}. + * + * @return this + */ + public CommandOutputControl saveFirstLineOfOutput() { + return setOutputControl(true, OutputControlOption.SAVE_FIRST_LINE); + } + + /** + * Returns whether this object will save the first line of the output of + * commands subsequently executed. + * + * @return {@code true} if this object will save the first line of the output of + * commands it executes subsequently; {@code false} otherwise + */ + public boolean isSaveFirstLineOfOutput() { + return outputStreamsControl.stdout().saveFirstLine(); + } + + /** + * Specifies whether this object will dump the output streams from + * subsequently executed commands into the streams returned by + * {@link #dumpStdout()} and {@link #dumpStdout()} methods respectively. + *

+ * If this object is configured to redirect stderr of subsequently executed + * commands into their stdout ({@code redirectStderr(true)}), their output + * streams will be dumped into the stream returned by {@code dumpStdout()} + * method. Otherwise, their stdout and stderr streams will be dumped into the + * stream returned by {@code dumpStdout()} and {@code dumpStderr()} methods + * respectively. + * + * @param v if output streams from subsequently executed commands will be + * dumped into streams returned by {@code dumpStdout()} and + * {@code dumpStderr()} methods respectively + * + * @return this + * + * @see #redirectStderr(boolean) + * @see #dumpStdout() + * @see #dumpStderr() + */ + public CommandOutputControl dumpOutput(boolean v) { + setFlag(Flag.DUMP, v); + return setOutputControl(v, OutputControlOption.DUMP); + } + + /** + * Returns the value passed in the last call of {@link #dumpOutput(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #dumpOutput(boolean)} + */ + public boolean isDumpOutput() { + return Flag.DUMP.isSet(flags); + } + + /** + * Specifies whether this object will treat output streams of subsequently + * executed commands as byte streams rather than character streams. + * + * @param v {@code true} if this object will treat the output streams of + * subsequently executed commands as byte streams, and {@code false} + * otherwise + * + * @return this + */ + public CommandOutputControl binaryOutput(boolean v) { + return setFlag(Flag.BINARY_OUTPUT, v); + } + + /** + * Returns the value passed in the last call of {@link #binaryOutput(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #binaryOutput(boolean)} + */ + public boolean isBinaryOutput() { + return Flag.BINARY_OUTPUT.isSet(flags); + } + + /** + * Sets character encoding that will be applied to the stdout and the stderr + * streams of commands (subprocesses and {@code ToolProvider}-s) subsequently + * executed by this object. The default encoding is {@code UTF-8}. + *

+ * The value will be ignored if this object is configured for byte output + * streams. + * + * @param v character encoding for output streams of subsequently executed + * commands + * + * @see #binaryOutput(boolean) + * + * @return this + */ + public CommandOutputControl charset(Charset v) { + charset = v; + return this; + } + + /** + * Returns the value passed in the last call of + * {@link #charset(Charset)} method on this object, or + * {@link StandardCharsets#UTF_8} if the method has not been called. + * + * @return the character encoding that will be applied to the stdout and stderr + * streams of commands subsequently executed by this object + */ + public Charset charset() { + return Optional.ofNullable(charset).orElse(StandardCharsets.UTF_8); + } + + /** + * Specifies whether the stderr stream will be redirected into the stdout stream + * for commands subsequently executed by this object. + * + * @see ProcessBuilder#redirectErrorStream(boolean) + * + * @param v {@code true} if the stderr stream of commands subsequently executed + * by this object will be redirected into the stdout stream; + * {@code false} otherwise + * + * @return this + */ + public CommandOutputControl redirectStderr(boolean v) { + return setFlag(Flag.REDIRECT_STDERR, v); + } + + /** + * Returns the value passed in the last call of {@link #redirectStderr(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #redirectStderr(boolean)} + */ + public boolean isRedirectStderr() { + return Flag.REDIRECT_STDERR.isSet(flags); + } + + /** + * Specifies whether stderr and stdout streams for subprocesses subsequently + * executed by this object will be stored in files. + *

+ * By default, if an output stream of a subprocess is configured for saving, + * this object will retrieve the content using {@link Process#getInputStream()} + * function for stdout and {@link Process#getErrorStream()} function for stderr. + * However, these functions don't always work correctly due to a + * JDK-8236825 bug + * still reproducible on macOS JDK26. The alternative way to get the content of + * output streams of a subprocess is to redirect them into files and read these + * files when the subprocess terminates. + *

+ * It will use {@code Files.createTempFile("jpackageOutputTempFile", ".tmp")} to + * create a file for each subprocess's output stream configured for saving. All + * created files will be automatically deleted at the exit of + * {@link #execute(ProcessBuilder, long)} method. + *

+ * Doesn't apply to executing {@code ToolProvider}-s. + *

+ * Storing output streams in files takes longer than managing them in memory and + * should be avoided if possible. + * + * @param v {@code true} if this object will use files to store saved output + * streams of subsequently executed subprocesses; {@code false} + * otherwise + * @return this + */ + public CommandOutputControl storeOutputInFiles(boolean v) { + return setFlag(Flag.STORE_OUTPUT_IN_FILES, v); + } + + /** + * Returns the value passed in the last call of {@link #storeOutputInFiles(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #storeOutputInFiles(boolean)} + */ + public boolean isStoreOutputInFiles() { + return Flag.STORE_OUTPUT_IN_FILES.isSet(flags); + } + + /** + * Specifies whether stdout streams from commands subsequently executed by this + * object will be discarded. + * + * @param v {@code true} if this object will discard stdout streams from + * commands subsequently executed by this object; {@code false} + * otherwise + * @return this + */ + public CommandOutputControl discardStdout(boolean v) { + setFlag(Flag.DISCARD_STDOUT, v); + outputStreamsControl.stdout().discard(v); + return this; + } + + /** + * Returns the value passed in the last call of {@link #discardStdout(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #discardStdout(boolean)} + */ + public boolean isDiscardStdout() { + return Flag.DISCARD_STDOUT.isSet(flags); + } + + /** + * Specifies whether stderr streams from commands subsequently executed by this + * object will be discarded. + * + * @param v {@code true} if this object will discard stderr streams from + * commands subsequently executed by this object; {@code false} + * otherwise + * @return this + */ + public CommandOutputControl discardStderr(boolean v) { + setFlag(Flag.DISCARD_STDERR, v); + outputStreamsControl.stderr().discard(v); + return this; + } + + /** + * Returns the value passed in the last call of {@link #discardStderr(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #discardStderr(boolean)} + */ + public boolean isDiscardStderr() { + return Flag.DISCARD_STDERR.isSet(flags); + } + + /** + * Specifies the stream where stdout streams from commands subsequently executed + * by this object will be dumped. + *

+ * If the {@code null} is specified and this object configuration is equivalent + * to {@code dumpOutput(true).saveOutput(false).discardStdout(false)} the stdout + * streams from commands subsequently executed by this object will be written + * into the file descriptor associated with the {@code Systsem.out} stream. If + * you want them to be written into the {@code Systsem.out} object, pass the + * {@code Systsem.out} reference into this function. + * + * @param v the stream where stdout streams from commands subsequently executed + * by this object will be dumped; {@code null} permitted + * @return this + */ + public CommandOutputControl dumpStdout(PrintStream v) { + dumpStdout = v; + return this; + } + + /** + * Returns the value passed in the last call of {@link #dumpStdout(PrintStream)} + * method on this object, or {@link System#out} if the method has not been + * called. + * + * @return the stream where stdout streams from commands subsequently executed + * by this object will be dumped + */ + public PrintStream dumpStdout() { + return Optional.ofNullable(dumpStdout).orElse(System.out); + } + + /** + * Specifies the stream where stderr streams from commands subsequently executed + * by this object will be dumped. + *

+ * If the {@code null} is specified and this object configuration is equivalent + * to + * {@code dumpOutput(true).saveOutput(false).redirectStderr(false).discardStderr(false)} + * the stderr streams from commands subsequently executed by this object will be + * written into the file descriptor associated with the {@code Systsem.err} + * stream. If you want them to be written into the {@code Systsem.err} object, + * pass the {@code Systsem.err} reference into this function. + * + * @param v the stream where stderr streams from commands subsequently executed + * by this object will be dumped; {@code null} permitted + * @return this + */ + public CommandOutputControl dumpStderr(PrintStream v) { + dumpStderr = v; + return this; + } + + /** + * Returns the value passed in the last call of {@link #dumpStderr(PrintStream)} + * method on this object, or {@link System#err} if the method has not been + * called. + * + * @return the stream where stderr streams from commands subsequently executed + * by this object will be dumped + */ + public PrintStream dumpStderr() { + return Optional.ofNullable(dumpStderr).orElse(System.err); + } + + /** + * Sets the callback to be invoked when this object starts a subprocess from + * subsequent {@link #execute(ProcessBuilder, long)} calls. + * + *

+ * This object maintains a single callback. Calling this method replaces any + * previously set callback. + * + *

+ * The callback is invoked on the thread that calls + * {@link #execute(ProcessBuilder, long)} after the subprocess's output streams + * begin being pumped. + * + * @param v the callback for notifying a subprocess being started or + * {@code null} + * @return this + */ + public CommandOutputControl processListener(Consumer v) { + processListener = v; + return this; + } + + /** + * Returns an {@code Optional} wrapping the value passed in the last call of + * {@link #processListener(Consumer)} method on this object, or an empty + * {@code Optional} if the method has not been called or {@code null} was passed in the last call. + * + * @return an {@code Optional} wrapping the value passed in the last call of + * {@link #processListener(Consumer)} + */ + public Optional> processListener() { + return Optional.ofNullable(processListener); + } + + /** + * Returns a deep copy of this object. Changes to the copy will not affect the + * original. + * + * @return a deep copy of this object + */ + public CommandOutputControl copy() { + return new CommandOutputControl(this); + } + + public interface ExecutableAttributes { + List commandLine(); + + default String printableCommandLine() { + return CommandLineFormat.DEFAULT.apply(commandLine()); + } + } + + public sealed interface Executable { + + ExecutableAttributes attributes(); + + Result execute() throws IOException, InterruptedException; + + Result execute(long timeout, TimeUnit unit) throws IOException, InterruptedException; + } + + public record ProcessAttributes(Optional pid, List commandLine) implements ExecutableAttributes { + public ProcessAttributes { + Objects.requireNonNull(pid); + commandLine.forEach(Objects::requireNonNull); + } + } + + public record ToolProviderAttributes(String name, List args) implements ExecutableAttributes { + public ToolProviderAttributes { + Objects.requireNonNull(name); + args.forEach(Objects::requireNonNull); + } + + @Override + public List commandLine() { + return Stream.concat(Stream.of(name), args.stream()).toList(); + } + } + + public static ExecutableAttributes EMPTY_EXECUTABLE_ATTRIBUTES = new ExecutableAttributes() { + @Override + public List commandLine() { + return List.of(""); + } + }; + + public Executable createExecutable(ToolProvider tp, String... args) { + return new ToolProviderExecutable(tp, List.of(args), this); + } + + public Executable createExecutable(ProcessBuilder pb) { + return new ProcessExecutable(pb, this); + } + + public record Result( + Optional exitCode, + Optional>> output, + Optional> byteOutput, + ExecutableAttributes execAttrs) { + + public Result { + Objects.requireNonNull(exitCode); + Objects.requireNonNull(output); + Objects.requireNonNull(byteOutput); + Objects.requireNonNull(execAttrs); + } + + public static Builder build() { + return new Builder(); + } + + public static final class Builder { + + public Result create() { + return new Result( + exitCode, + Optional.ofNullable(content).map(List::copyOf).map(StringListContent::new).map(c -> { + return new CommandOutput>(Optional.of(c), c.size(), true); + }), + Optional.empty(), + Optional.ofNullable(execAttrs).orElse(EMPTY_EXECUTABLE_ATTRIBUTES)); + } + + public Builder exitCode(int v) { + exitCode = Optional.of(v); + return this; + } + + public Builder noExitCode() { + exitCode = Optional.empty(); + return this; + } + + public Builder execAttrs(ExecutableAttributes v) { + execAttrs = v; + return this; + } + + public Builder content(List v) { + content = v; + return this; + } + + public Builder content(String... v) { + return content(List.of(v)); + } + + private ExecutableAttributes execAttrs; + private List content; + private Optional exitCode = Optional.empty(); + } + + public Result(int exitCode) { + this(Optional.of(exitCode), Optional.empty(), Optional.empty(), EMPTY_EXECUTABLE_ATTRIBUTES); + } + + public int getExitCode() { + return exitCode.orElseThrow(() -> { + return new IllegalStateException("Exit code is unavailable for timed-out command"); + }); + } + + public Result expectExitCode(int main, int... other) throws UnexpectedExitCodeException { + return expectExitCode(v -> { + return IntStream.concat(IntStream.of(main), IntStream.of(other)).boxed().anyMatch(Predicate.isEqual(v)); + }); + } + + public Result expectExitCode(Collection expected) throws UnexpectedExitCodeException { + return expectExitCode(expected::contains); + } + + public Result expectExitCode(IntPredicate expected) throws UnexpectedExitCodeException { + if (!expected.test(getExitCode())) { + throw new UnexpectedExitCodeException(this); + } + return this; + } + + public UnexpectedResultException unexpected() { + return new UnexpectedResultException(this); + } + + public UnexpectedResultException unexpected(String message) { + return new UnexpectedResultException(this, message); + } + + public Optional> findContent() { + return output.flatMap(CommandOutput::combined); + } + + public Optional> findStdout() { + return output.flatMap(CommandOutput::stdout); + } + + public Optional> findStderr() { + return output.flatMap(CommandOutput::stderr); + } + + // For backward compatibility + public List getOutput() { + return content(); + } + + public List content() { + return findContent().orElseThrow(); + } + + public List stdout() { + return findStdout().orElseThrow(); + } + + public List stderr() { + return findStderr().orElseThrow(); + } + + public Optional findByteContent() { + return byteOutput.flatMap(CommandOutput::combined); + } + + public Optional findByteStdout() { + return byteOutput.flatMap(CommandOutput::stdout); + } + + public Optional findByteStderr() { + return byteOutput.flatMap(CommandOutput::stderr); + } + + public byte[] byteContent() { + return findByteContent().orElseThrow(); + } + + public byte[] byteStdout() { + return findByteStdout().orElseThrow(); + } + + public byte[] byteStderr() { + return findByteStderr().orElseThrow(); + } + + public Result toCharacterResult(Charset charset, boolean keepByteContent) throws IOException { + Objects.requireNonNull(charset); + + if (byteOutput.isEmpty()) { + return this; + } + + var theByteOutput = byteOutput.get(); + + try { + Optional>> out; + if (theByteOutput.content().isEmpty()) { + // The content is unavailable. + out = Optional.empty(); + } else if (theByteOutput.stdoutContentSize() == 0) { + // The content is available, but empty. + out = Optional.of(new StringListContent(List.of())); + } else if (theByteOutput.interleaved()) { + // STDOUT and STDERR streams are interleaved. + out = theByteOutput.combined().map(data -> { + return toStringList(data, charset); + }); + } else { + // Non-empty STDOUT not interleaved with STDERR. + out = findByteStdout().map(data -> { + return toStringList(data, charset); + }); + } + + var err = findByteStderr().map(data -> { + return toStringList(data, charset); + }); + + var newOutput = combine(out, err, theByteOutput.interleaved); + + return new Result(exitCode, Optional.of(newOutput), byteOutput.filter(_ -> keepByteContent), execAttrs); + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + + public Result copyWithExecutableAttributes(ExecutableAttributes execAttrs) { + return new Result(exitCode, output, byteOutput, Objects.requireNonNull(execAttrs)); + } + + private static StringListContent toStringList(byte[] data, Charset charset) { + try (var bufReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data), charset))) { + return new StringListContent(bufReader.lines().toList()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } + + public static sealed class UnexpectedResultException extends IOException { + + private UnexpectedResultException(Result value, String message) { + super(Objects.requireNonNull(message)); + this.value = Objects.requireNonNull(value); + } + + private UnexpectedResultException(Result value) { + this(value, String.format("Unexpected result from executing the command %s", + value.execAttrs().printableCommandLine())); + } + + public Result getResult() { + return value; + } + + private final transient Result value; + + private static final long serialVersionUID = 1L; + } + + public static final class UnexpectedExitCodeException extends UnexpectedResultException { + + public UnexpectedExitCodeException(Result value, String message) { + super(requireExitCode(value), message); + } + + public UnexpectedExitCodeException(Result value) { + this(value, String.format("Unexpected exit code %d from executing the command %s", + requireExitCode(value).getExitCode(), value.execAttrs().printableCommandLine())); + } + + private static Result requireExitCode(Result v) { + Objects.requireNonNull(v); + if (v.exitCode().isPresent()) { + return v; + } else { + throw new IllegalArgumentException(); + } + } + + private static final long serialVersionUID = 1L; + } + + public String description() { + var tokens = outputStreamsControl.descriptionTokens(); + if (isBinaryOutput()) { + tokens.add("byte"); + } + if (redirectRetainedStderr()) { + tokens.add("interleave"); + } + return String.join("; ", tokens); + } + + private Result execute(ProcessBuilder pb, long timeoutMillis) + throws IOException, InterruptedException { + + Objects.requireNonNull(pb); + + var theCharset = charset(); + + configureProcessBuilder(pb); + + var csc = new CachingStreamsConfig(); + + var process = pb.start(); + + BiConsumer gobbler = (in, ps) -> { + try { + if (isBinaryOutput()) { + try (in) { + in.transferTo(ps); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } else { + try (var bufReader = new BufferedReader(new InputStreamReader(in, theCharset))) { + bufReader.lines().forEach(ps::println); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } finally { + suppressIOException(ps::flush); + } + }; + + // Start fetching process output streams. + // Do it before waiting for the process termination to avoid deadlocks. + + final Optional> stdoutGobbler; + if (mustReadOutputStream(pb.redirectOutput())) { + stdoutGobbler = Optional.of(CompletableFuture.runAsync(() -> { + gobbler.accept(process.getInputStream(), csc.out()); + }, gobblerExecutor)); + } else { + stdoutGobbler = Optional.empty(); + } + + final Optional> stderrGobbler; + if (!pb.redirectErrorStream() && mustReadOutputStream(pb.redirectError())) { + stderrGobbler = Optional.of(CompletableFuture.runAsync(() -> { + gobbler.accept(process.getErrorStream(), csc.err()); + }, gobblerExecutor)); + } else { + stderrGobbler = Optional.empty(); + } + + processListener().ifPresent(c -> { + c.accept(process); + }); + + final Optional exitCode; + if (timeoutMillis < 0) { + exitCode = Optional.of(process.waitFor()); + } else if (!process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS)) { + // Destroy the process and cancel the process output stream gobblers. + process.destroy(); + for (var g : List.of(stdoutGobbler, stderrGobbler)) { + g.ifPresent(future -> { + future.cancel(true); + }); + } + exitCode = Optional.empty(); + } else { + exitCode = Optional.of(process.exitValue()); + } + + try { + if (isStoreOutputInFiles()) { + var stdoutStorage = streamFileSink(pb.redirectOutput()); + var stderrStorage = streamFileSink(pb.redirectError()); + + Function toInputStream = path -> { + try { + return Files.newInputStream(path); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }; + + try { + stdoutStorage.map(toInputStream).ifPresent(in -> { + gobbler.accept(in, csc.out()); + }); + + stderrStorage.map(toInputStream).ifPresent(in -> { + gobbler.accept(in, csc.err()); + }); + } finally { + Consumer silentDeleter = path -> { + suppressIOException(Files::delete, path); + }; + + stdoutStorage.ifPresent(silentDeleter); + stderrStorage.ifPresent(silentDeleter); + } + } else { + stdoutGobbler.ifPresent(CommandOutputControl::join); + stderrGobbler.ifPresent(CommandOutputControl::join); + } + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + + return csc.createResult(exitCode, new ProcessAttributes(getPID(process), pb.command())); + } + + private Result execute(ToolProvider tp, long timeoutMillis, String... args) + throws IOException, InterruptedException { + + var csc = new CachingStreamsConfig(); + + Optional exitCode; + var out = csc.out(); + var err = csc.err(); + try { + if (timeoutMillis < 0) { + exitCode = Optional.of(tp.run(out, err, args)); + } else { + var future = new CompletableFuture>(); + + var workerThread = Thread.ofVirtual().start(() -> { + Optional result = Optional.empty(); + try { + result = Optional.of(tp.run(out, err, args)); + } catch (Exception ex) { + future.completeExceptionally(ex); + return; + } + future.complete(result); + }); + + try { + exitCode = future.get(timeoutMillis, TimeUnit.MILLISECONDS); + } catch (ExecutionException ex) { + // Rethrow the cause (ex.getCause()) as a RuntimeException. + // If `ex.getCause()` returns an Error, ExceptionBox.unbox() will throw it. + throw ExceptionBox.toUnchecked(ExceptionBox.unbox(ex.getCause())); + } catch (TimeoutException ex) { + workerThread.interrupt(); + exitCode = Optional.empty(); + } + } + } finally { + suppressIOException(out::flush); + suppressIOException(err::flush); + } + + return csc.createResult(exitCode, new ToolProviderAttributes(tp.name(), List.of(args))); + } + + private CommandOutputControl setOutputControl(boolean set, OutputControlOption v) { + outputStreamsControl.stdout().set(set, v); + outputStreamsControl.stderr().set(set, v); + return this; + } + + private CommandOutputControl setFlag(Flag flag, boolean v) { + flags = flag.set(flags, v); + return this; + } + + private Optional streamFileSink(ProcessBuilder.Redirect redirect) { + return Optional.of(redirect) + .filter(Predicate.isEqual(ProcessBuilder.Redirect.DISCARD).negate()) + .map(ProcessBuilder.Redirect::file) + .map(File::toPath); + } + + private void configureProcessBuilder(ProcessBuilder pb) throws IOException { + + var stdoutRedirect = outputStreamsControl.stdout().asProcessBuilderRedirect(); + var stderrRedirect = outputStreamsControl.stderr().asProcessBuilderRedirect(); + + if (!stdoutRedirect.equals(stderrRedirect) && Stream.of( + stdoutRedirect, + stderrRedirect + ).noneMatch(Predicate.isEqual(ProcessBuilder.Redirect.DISCARD)) && redirectRetainedStderr()) { + throw new IllegalStateException(String.format( + "Can't redirect stderr into stdout because they have different redirects: stdout=%s; stderr=%s", + stdoutRedirect, stderrRedirect)); + } + + pb.redirectErrorStream(redirectRetainedStderr()); + if (replaceStdoutWithStderr()) { + if (stderrRedirect.equals(ProcessBuilder.Redirect.INHERIT)) { + stderrRedirect = ProcessBuilder.Redirect.PIPE; + } + pb.redirectErrorStream(false); + } + + stdoutRedirect = mapRedirect(stdoutRedirect); + stderrRedirect = mapRedirect(stderrRedirect); + + if (dumpStdout != null && stdoutRedirect.equals(ProcessBuilder.Redirect.INHERIT)) { + stdoutRedirect = ProcessBuilder.Redirect.PIPE; + } + + if (dumpStderr != null && stderrRedirect.equals(ProcessBuilder.Redirect.INHERIT)) { + stderrRedirect = ProcessBuilder.Redirect.PIPE; + } + + pb.redirectOutput(stdoutRedirect); + pb.redirectError(stderrRedirect); + } + + private ProcessBuilder.Redirect mapRedirect(ProcessBuilder.Redirect redirect) throws IOException { + if (isStoreOutputInFiles() && redirect.equals(ProcessBuilder.Redirect.PIPE)) { + var sink = Files.createTempFile("jpackageOutputTempFile", ".tmp"); + return ProcessBuilder.Redirect.to(sink.toFile()); + } else { + return redirect; + } + } + + /** + * Returns {@code true} if STDERR is not discarded and will be redirected to STDOUT, and {@code false} otherwise. + */ + private boolean redirectRetainedStderr() { + return isRedirectStderr() && !outputStreamsControl.stderr().discard(); + } + + /** + * Returns {@code true} if STDERR will replace STDOUT, and {@code false} otherwise. + *

+ * STDERR will replace STDOUT if it is redirected and not discarded, and if STDOUT is discarded. + */ + private boolean replaceStdoutWithStderr() { + return redirectRetainedStderr() && outputStreamsControl.stdout().discard(); + } + + private static T join(CompletableFuture future, T cancelledValue) { + Objects.requireNonNull(future); + try { + return future.join(); + } catch (CancellationException ex) { + return cancelledValue; + } catch (CompletionException ex) { + switch (ExceptionBox.unbox(ex.getCause())) { + case IOException cause -> { + throw new UncheckedIOException(cause); + } + case UncheckedIOException cause -> { + throw cause; + } + case Exception cause -> { + throw ExceptionBox.toUnchecked(cause); + } + } + } + } + + private static void join(CompletableFuture future) { + join(future, null); + } + + private static boolean mustReadOutputStream(ProcessBuilder.Redirect redirect) { + return redirect.equals(ProcessBuilder.Redirect.PIPE); + } + + private static Optional> read(OutputControl outputControl, CachingPrintStream cps) throws IOException { + final var bufferAsString = cps.bufferContents(); + try (final var bufReader = new BufferedReader(new StringReader(bufferAsString.orElse("")))) { + if (outputControl.saveFirstLine()) { + return Optional.of(bufReader.lines().findFirst().map(List::of).orElseGet(List::of)); + } else if (outputControl.saveAll()) { + return Optional.of(bufReader.lines().toList()); + } else { + return Optional.empty(); + } + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + + private static Optional readBinary(OutputControl outputControl, CachingPrintStream cps) { + if (outputControl.save()) { + return cps.buf().map(ByteArrayOutputStream::toByteArray).or(() -> { + return Optional.of(new byte[0]); + }); + } else { + return Optional.empty(); + } + } + + private static CommandOutput combine( + Optional> out, + Optional> err, + boolean interleaved) { + + if (out.isEmpty() && err.isEmpty()) { + return CommandOutput.empty(); + } else if (out.isEmpty()) { + // This branch is unreachable because it is impossible to make it save stderr without saving stdout. + // If streams are configured for saving and stdout is discarded, + // its saved contents will be an Optional instance wrapping an empty content, not an empty Optional. + throw ExceptionBox.reachedUnreachable(); + } else if (err.isEmpty()) { + return new CommandOutput<>(out, Integer.MAX_VALUE, interleaved); + } else { + final var combined = out.get().append(err.get()); + return new CommandOutput<>(Optional.of(combined), out.orElseThrow().size(), interleaved); + } + } + + private static PrintStream nullPrintStream() { + return new PrintStream(OutputStream.nullOutputStream()); + } + + private sealed interface Content { + T data(); + int size(); + Content slice(int from, int to); + Content append(Content other); + } + + private record StringListContent(List data) implements Content> { + StringListContent { + Objects.requireNonNull(data); + } + + @Override + public int size() { + return data.size(); + } + + @Override + public StringListContent slice(int from, int to) { + return new StringListContent(data.subList(from, to)); + } + + @Override + public StringListContent append(Content> other) { + return new StringListContent(Stream.of(data, other.data()).flatMap(List::stream).toList()); + } + } + + private record ByteContent(byte[] data) implements Content { + ByteContent { + Objects.requireNonNull(data); + } + + @Override + public int size() { + return data.length; + } + + @Override + public ByteContent slice(int from, int to) { + return new ByteContent(Arrays.copyOfRange(data, from, to)); + } + + @Override + public ByteContent append(Content other) { + byte[] combined = new byte[size() + other.size()]; + System.arraycopy(data, 0, combined, 0, data.length); + System.arraycopy(other.data(), 0, combined, data.length, other.size()); + return new ByteContent(combined); + } + } + + private record OutputStreamsControl(OutputControl stdout, OutputControl stderr) { + OutputStreamsControl { + Objects.requireNonNull(stdout); + Objects.requireNonNull(stderr); + } + + OutputStreamsControl() { + this(new OutputControl(), new OutputControl()); + } + + OutputStreamsControl copy() { + return new OutputStreamsControl(stdout.copy(), stderr.copy()); + } + + List descriptionTokens() { + final List tokens = new ArrayList<>(); + if (stdout.save()) { // Save flags are the same for stdout and stderr, checking stdout is sufficient. + streamsLabel("save ", true).ifPresent(tokens::add); + } + if (stdout.dump() || stderr.dump()) { + streamsLabel("echo ", true).ifPresent(tokens::add); + } + streamsLabel("discard ", false).ifPresent(tokens::add); + if (tokens.isEmpty()) { + // Unreachable because there is always at least one token in the description. + throw ExceptionBox.reachedUnreachable(); + } else { + return tokens; + } + } + + private Optional streamsLabel(String prefix, boolean negate) { + Objects.requireNonNull(prefix); + final var str = Stream.of(stdoutLabel(negate), stderrLabel(negate)) + .filter(Optional::isPresent) + .map(Optional::orElseThrow) + .collect(joining("+")); + if (str.isEmpty()) { + return Optional.empty(); + } else { + return Optional.of(prefix + str); + } + } + + private Optional stdoutLabel(boolean negate) { + if ((stdout.discard() && !negate) || (!stdout.discard() && negate)) { + return Optional.of("out"); + } else { + return Optional.empty(); + } + } + + private Optional stderrLabel(boolean negate) { + if ((stderr.discard() && !negate) || (!stderr.discard() && negate)) { + return Optional.of("err"); + } else { + return Optional.empty(); + } + } + } + + private record CachingPrintStream(PrintStream ps, Optional buf) { + CachingPrintStream { + Objects.requireNonNull(ps); + Objects.requireNonNull(buf); + } + + Optional bufferContents() { + return buf.map(ByteArrayOutputStream::toString); + } + + static Builder build(Charset charset) { + return new Builder(charset); + } + + static final class Builder { + + private Builder(Charset charset) { + this.charset = Objects.requireNonNull(charset); + } + + Builder save(boolean v) { + save = v; + return this; + } + + Builder discard(boolean v) { + discard = v; + return this; + } + + Builder dumpStream(PrintStream v) { + dumpStream = v; + return this; + } + + Builder buffer(ByteArrayOutputStream v) { + externalBuffer = v; + return this; + } + + CachingPrintStream create() { + final Optional buf; + if (save && !discard) { + buf = Optional.ofNullable(externalBuffer).or(() -> { + return Optional.of(new ByteArrayOutputStream()); + }); + } else { + buf = Optional.empty(); + } + + final PrintStream ps; + if (buf.isPresent() && dumpStream != null) { + ps = new PrintStream(new TeeOutputStream(List.of(buf.get(), dumpStream)), true, dumpStream.charset()); + } else if (!discard) { + ps = buf.map(in -> { + return new PrintStream(in, false, charset); + }).or(() -> { + return Optional.ofNullable(dumpStream); + }).orElseGet(CommandOutputControl::nullPrintStream); + } else { + ps = nullPrintStream(); + } + + return new CachingPrintStream(ps, buf); + } + + private boolean save; + private boolean discard; + private PrintStream dumpStream; + private ByteArrayOutputStream externalBuffer; + private final Charset charset; + } + } + + private final class CachingStreamsConfig { + + CachingStreamsConfig() { + out = outputStreamsControl.stdout().buildCachingPrintStream(dumpStdout(), charset()).create(); + if (isRedirectStderr()) { + var builder = outputStreamsControl.stderr().buildCachingPrintStream(dumpStdout(), charset()); + out.buf().ifPresent(builder::buffer); + err = builder.create(); + } else { + err = outputStreamsControl.stderr().buildCachingPrintStream(dumpStderr(), charset()).create(); + } + } + + Result createResult(Optional exitCode, ExecutableAttributes execAttrs) throws IOException { + + CommandOutput> output; + CommandOutput byteOutput; + + CachingPrintStream effectiveOut; + if (out.buf().isEmpty() && isRedirectStderr()) { + effectiveOut = new CachingPrintStream(nullPrintStream(), err.buf()); + } else { + effectiveOut = out; + } + + if (isBinaryOutput()) { + Optional outContent, errContent; + if (isRedirectStderr()) { + outContent = readBinary(outputStreamsControl.stdout(), effectiveOut).map(ByteContent::new); + errContent = Optional.empty(); + } else { + outContent = readBinary(outputStreamsControl.stdout(), out).map(ByteContent::new); + errContent = readBinary(outputStreamsControl.stderr(), err).map(ByteContent::new); + } + + byteOutput = combine(outContent, errContent, redirectRetainedStderr()); + output = null; + } else { + Optional outContent, errContent; + if (isRedirectStderr()) { + outContent = read(outputStreamsControl.stdout(), effectiveOut).map(StringListContent::new); + errContent = Optional.empty(); + } else { + outContent = read(outputStreamsControl.stdout(), out).map(StringListContent::new); + errContent = read(outputStreamsControl.stderr(), err).map(StringListContent::new); + } + + output = combine(outContent, errContent, redirectRetainedStderr()); + byteOutput = null; + } + + return new Result(exitCode, Optional.ofNullable(output), Optional.ofNullable(byteOutput), execAttrs); + } + + PrintStream out() { + return out.ps(); + } + + PrintStream err() { + return err.ps(); + } + + private final CachingPrintStream out; + private final CachingPrintStream err; + } + + private static final class OutputControl { + + OutputControl() { + } + + private OutputControl(OutputControl other) { + dump = other.dump; + discard = other.discard; + save = other.save; + } + + boolean save() { + return save.isPresent(); + } + + boolean saveAll() { + return save.orElse(null) == OutputControlOption.SAVE_ALL; + } + + boolean saveFirstLine() { + return save.orElse(null) == OutputControlOption.SAVE_FIRST_LINE; + } + + boolean discard() { + return discard || (!dump && save.isEmpty()); + } + + boolean dump() { + return !discard && dump; + } + + OutputControl dump(boolean v) { + this.dump = v; + return this; + } + + OutputControl discard(boolean v) { + this.discard = v; + return this; + } + + OutputControl saveAll(boolean v) { + if (v) { + save = Optional.of(OutputControlOption.SAVE_ALL); + } else { + save = Optional.empty(); + } + return this; + } + + OutputControl saveFirstLine(boolean v) { + if (v) { + save = Optional.of(OutputControlOption.SAVE_FIRST_LINE); + } else { + save = Optional.empty(); + } + return this; + } + + OutputControl set(boolean set, OutputControlOption v) { + switch (v) { + case DUMP -> dump(set); + case SAVE_ALL -> saveAll(set); + case SAVE_FIRST_LINE -> saveFirstLine(set); + } + return this; + } + + OutputControl copy() { + return new OutputControl(this); + } + + ProcessBuilder.Redirect asProcessBuilderRedirect() { + if (discard()) { + return ProcessBuilder.Redirect.DISCARD; + } else if (dump && !save()) { + return ProcessBuilder.Redirect.INHERIT; + } else { + return ProcessBuilder.Redirect.PIPE; + } + } + + CachingPrintStream.Builder buildCachingPrintStream(PrintStream dumpStream, Charset charset) { + Objects.requireNonNull(dumpStream); + final var builder = CachingPrintStream.build(charset).save(save()).discard(discard()); + if (dump()) { + builder.dumpStream(dumpStream); + } + return builder; + } + + private boolean dump; + private boolean discard; + private Optional save = Optional.empty(); + } + + private record CommandOutput(Optional> content, int stdoutContentSize, boolean interleaved) { + + CommandOutput { + Objects.requireNonNull(content); + if (interleaved) { + stdoutContentSize = content.map(Content::size).orElse(-1); + } + } + + CommandOutput() { + this(Optional.empty(), 0, false); + } + + Optional combined() { + return content.map(Content::data); + } + + /** + * Returns non-empty {@code Optional} if stdout is available and stdout and stderr are not interleaved. + * @return stdout if it can be extracted from the combined output + */ + Optional stdout() { + if (withoutExtractableStdout()) { + return Optional.empty(); + } + + final var theContent = content.orElseThrow(); + if (stdoutContentSize == theContent.size()) { + return combined(); + } else { + return Optional.of(theContent.slice(0, Integer.min(stdoutContentSize, theContent.size())).data()); + } + } + + /** + * Returns non-empty {@code Optional} if stderr is available and stdout and stderr are not interleaved. + * @return stderr if it can be extracted from the combined output + */ + Optional stderr() { + if (withoutExtractableStderr()) { + return Optional.empty(); + } else if (stdoutContentSize <= 0) { + return combined(); + } else { + final var theContent = content.orElseThrow(); + return Optional.of(theContent.slice(stdoutContentSize, theContent.size()).data()); + } + } + + @SuppressWarnings("unchecked") + static CommandOutput empty() { + return (CommandOutput)EMPTY; + } + + private boolean withoutExtractableStdout() { + return interleaved || content.isEmpty() || stdoutContentSize < 0; + } + + private boolean withoutExtractableStderr() { + return interleaved || content.isEmpty() || stdoutContentSize > content.get().size(); + } + + private static final CommandOutput EMPTY = new CommandOutput<>(); + } + + private record ToolProviderExecutable(ToolProvider tp, List args, CommandOutputControl coc) implements Executable { + + ToolProviderExecutable { + Objects.requireNonNull(tp); + Objects.requireNonNull(args); + Objects.requireNonNull(coc); + } + + @Override + public Result execute() throws IOException, InterruptedException { + return coc.execute(tp, -1, args.toArray(String[]::new)); + } + + @Override + public Result execute(long timeout, TimeUnit unit) throws IOException, InterruptedException { + return coc.execute(tp, unit.toMillis(timeout), args.toArray(String[]::new)); + } + + @Override + public ExecutableAttributes attributes() { + return new ToolProviderAttributes(tp.name(), args); + } + } + + private record ProcessExecutable(ProcessBuilder pb, CommandOutputControl coc) implements Executable { + + ProcessExecutable { + Objects.requireNonNull(pb); + Objects.requireNonNull(coc); + } + + @Override + public Result execute() throws IOException, InterruptedException { + return coc.execute(pb, -1L); + } + + @Override + public Result execute(long timeout, TimeUnit unit) throws IOException, InterruptedException { + return coc.execute(pb, unit.toMillis(timeout)); + } + + @Override + public ExecutableAttributes attributes() { + return new ProcessAttributes(Optional.empty(), pb.command()); + } + } + + private static Optional getPID(Process p) { + try { + return Optional.of(p.pid()); + } catch (UnsupportedOperationException ex) { + return Optional.empty(); + } + } + + private static void suppressIOException(ThrowingRunnable r) { + try { + r.run(); + } catch (IOException ex) {} + } + + private static void suppressIOException(ThrowingConsumer c, T value) { + suppressIOException(() -> { + c.accept(value); + }); + } + + private int flags; + private final OutputStreamsControl outputStreamsControl; + private PrintStream dumpStdout; + private PrintStream dumpStderr; + private Charset charset; + private Consumer processListener; + + // Executor to run subprocess output stream gobblers. + // Output stream gobblers should start fetching output streams ASAP after the process starts. + // No pooling, no waiting. + // CompletableFuture#runAsync() method starts an output stream gobbler. + // If used with the default executor, it is known to make WiX3 light.exe create + // a locked msi file when multiple jpackage tool providers are executed asynchronously. + // The AsyncTest fails with cryptic java.nio.file.FileSystemException error: + // jtreg_open_test_jdk_tools_jpackage_share_AsyncTest_java\\tmp\\jdk.jpackage8108811639097525318\\msi\\Foo-1.0.msi: The process cannot access the file because it is being used by another process. + // The remedy for the problem is to use non-pooling executor to run subprocess output stream gobblers. + private final java.util.concurrent.Executor gobblerExecutor = Executors.newVirtualThreadPerTaskExecutor(); + + private enum OutputControlOption { + SAVE_ALL, SAVE_FIRST_LINE, DUMP + } + + private enum Flag { + DUMP (0x01), + REDIRECT_STDERR (0x02), + BINARY_OUTPUT (0x04), + STORE_OUTPUT_IN_FILES (0x08), + DISCARD_STDOUT (0x10), + DISCARD_STDERR (0x20), + ; + + Flag(int value) { + this.value = value; + } + + int set(int flags, boolean set) { + if (set) { + return flags | value; + } else { + return flags & ~value; + } + } + + boolean isSet(int flags) { + return (flags & value) != 0; + } + + private final int value; + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Enquoter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Enquoter.java similarity index 77% rename from src/jdk.jpackage/share/classes/jdk/jpackage/internal/Enquoter.java rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Enquoter.java index 51f97ad2cd6..d9ededcbadb 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Enquoter.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Enquoter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.jpackage.internal; +package jdk.jpackage.internal.util; import java.util.Optional; import java.util.function.BiConsumer; @@ -32,39 +32,43 @@ import java.util.regex.Pattern; /** * Add quotes to the given string in a configurable way. */ -final class Enquoter { +public final class Enquoter { private Enquoter() { setQuoteChar('"'); } - static Enquoter forPropertyValues() { + public static Enquoter identity() { + return new Enquoter(); + } + + public static Enquoter forPropertyValues() { return new Enquoter() .setEnquotePredicate(QUOTE_IF_WHITESPACES) .setEscaper(PREPEND_BACKSLASH); } - static Enquoter forShellLiterals() { + public static Enquoter forShellLiterals() { return forShellLiterals('\''); } - static Enquoter forShellLiterals(char quoteChar) { + public static Enquoter forShellLiterals(char quoteChar) { return new Enquoter() .setQuoteChar(quoteChar) .setEnquotePredicate(x -> true) .setEscaper(PREPEND_BACKSLASH); } - String applyTo(String v) { + public String applyTo(String v) { if (!needQuotes.test(v)) { return v; } else { var buf = new StringBuilder(); buf.appendCodePoint(beginQuoteChr); - Optional.of(escaper).ifPresentOrElse(op -> { + Optional.ofNullable(escaper).ifPresentOrElse(op -> { v.codePoints().forEachOrdered(chr -> { if (chr == beginQuoteChr || chr == endQuoteChr) { - escaper.accept(chr, buf); + op.accept(chr, buf); } else { buf.appendCodePoint(chr); } @@ -77,28 +81,23 @@ final class Enquoter { } } - Enquoter setQuoteChar(char chr) { + public Enquoter setQuoteChar(char chr) { beginQuoteChr = chr; endQuoteChr = chr; return this; } - Enquoter setEscaper(BiConsumer v) { + public Enquoter setEscaper(BiConsumer v) { escaper = v; return this; } - Enquoter setEnquotePredicate(Predicate v) { + public Enquoter setEnquotePredicate(Predicate v) { needQuotes = v; return this; } - private int beginQuoteChr; - private int endQuoteChr; - private BiConsumer escaper; - private Predicate needQuotes = str -> false; - - private static final Predicate QUOTE_IF_WHITESPACES = new Predicate() { + public static final Predicate QUOTE_IF_WHITESPACES = new Predicate() { @Override public boolean test(String t) { return pattern.matcher(t).find(); @@ -106,8 +105,13 @@ final class Enquoter { private final Pattern pattern = Pattern.compile("\\s"); }; - private static final BiConsumer PREPEND_BACKSLASH = (chr, buf) -> { + public static final BiConsumer PREPEND_BACKSLASH = (chr, buf) -> { buf.append('\\'); buf.appendCodePoint(chr); }; + + private int beginQuoteChr; + private int endQuoteChr; + private BiConsumer escaper; + private Predicate needQuotes = str -> false; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java index 49c395642f4..b143a54cb5c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import jdk.internal.util.OperatingSystem; -import jdk.jpackage.internal.util.function.ExceptionBox; import jdk.jpackage.internal.util.function.ThrowingConsumer; public final class FileUtils { @@ -58,12 +57,6 @@ public final class FileUtils { public static void copyRecursive(Path src, Path dest, CopyOption... options) throws IOException { - copyRecursive(src, dest, List.of(), options); - } - - public static void copyRecursive(Path src, Path dest, - final List excludes, CopyOption... options) - throws IOException { List copyActions = new ArrayList<>(); @@ -72,24 +65,18 @@ public final class FileUtils { @Override public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) { - if (isPathMatch(dir, excludes)) { - return FileVisitResult.SKIP_SUBTREE; - } else { - copyActions.add(new CopyAction(null, dest.resolve(src.relativize(dir)))); - return FileVisitResult.CONTINUE; - } + copyActions.add(new CopyAction(null, dest.resolve(src.relativize(dir)))); + return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) { - if (!isPathMatch(file, excludes)) { - copyActions.add(new CopyAction(file, dest.resolve(src.relativize(file)))); - } + copyActions.add(new CopyAction(file, dest.resolve(src.relativize(file)))); return FileVisitResult.CONTINUE; } }); - } else if (!isPathMatch(src, excludes)) { + } else { Optional.ofNullable(dest.getParent()).ifPresent(dstDir -> { copyActions.add(new CopyAction(null, dstDir)); }); @@ -114,10 +101,6 @@ public final class FileUtils { } } - private static boolean isPathMatch(Path what, List paths) { - return paths.stream().anyMatch(what::endsWith); - } - private static record CopyAction(Path src, Path dest) { void apply(CopyOption... options) throws IOException { @@ -170,15 +153,13 @@ public final class FileUtils { } } - private void runActionOnPath(ThrowingConsumer action, Path path) { + private void runActionOnPath(ThrowingConsumer action, Path path) { try { action.accept(path); } catch (IOException ex) { if (this.ex == null) { this.ex = ex; } - } catch (Throwable t) { - throw ExceptionBox.rethrowUnchecked(t); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/IdentityWrapper.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/IdentityWrapper.java index 0a7a8cc8d4b..7b4c75630ac 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/IdentityWrapper.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/IdentityWrapper.java @@ -69,5 +69,9 @@ public final class IdentityWrapper { return String.format("Identity[%s]", value); } + public static IdentityWrapper wrapIdentity(T v) { + return new IdentityWrapper<>(v); + } + private final T value; } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MacBundle.java similarity index 63% rename from src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MacBundle.java index 723614f9bd6..95629e7d4b5 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MacBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,54 +23,49 @@ * questions. */ -package jdk.jpackage.internal; +package jdk.jpackage.internal.util; import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; -import jdk.jpackage.internal.model.AppImageLayout; /** * An abstraction of macOS Application bundle. * * @see https://en.wikipedia.org/wiki/Bundle_(macOS)#Application_bundles */ -record MacBundle(Path root) { +public record MacBundle(Path root) { - MacBundle { + public MacBundle { Objects.requireNonNull(root); } - boolean isValid() { + public boolean isValid() { return Files.isDirectory(contentsDir()) && Files.isDirectory(macOsDir()) && Files.isRegularFile(infoPlistFile()); } - boolean isSigned() { - return Files.isDirectory(contentsDir().resolve("_CodeSignature")); - } - - Path contentsDir() { + public Path contentsDir() { return root.resolve("Contents"); } - Path homeDir() { + public Path homeDir() { return contentsDir().resolve("Home"); } - Path macOsDir() { + public Path macOsDir() { return contentsDir().resolve("MacOS"); } - Path resourcesDir() { + public Path resourcesDir() { return contentsDir().resolve("Resources"); } - Path infoPlistFile() { + public Path infoPlistFile() { return contentsDir().resolve("Info.plist"); } - static Optional fromPath(Path path) { + public static Optional fromPath(Path path) { var bundle = new MacBundle(path); if (bundle.isValid()) { return Optional.of(bundle); @@ -78,20 +73,4 @@ record MacBundle(Path root) { return Optional.empty(); } } - - static Optional fromAppImageLayout(AppImageLayout layout) { - final var root = layout.rootDirectory(); - final var bundleSubdir = root.relativize(layout.runtimeDirectory()); - final var contentsDirname = Path.of("Contents"); - var bundleRoot = root; - for (int i = 0; i != bundleSubdir.getNameCount(); i++) { - var nameComponent = bundleSubdir.getName(i); - if (contentsDirname.equals(nameComponent)) { - return Optional.of(new MacBundle(bundleRoot)); - } else { - bundleRoot = bundleRoot.resolve(nameComponent); - } - } - return Optional.empty(); - } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.java index 2c693939dab..4c85358d424 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.dom.DOMSource; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; @@ -311,7 +310,7 @@ public final class PListReader { } } - public PListReader(byte[] xmlData) throws ParserConfigurationException, SAXException, IOException { + public PListReader(byte[] xmlData) throws SAXException, IOException { this(XmlUtils.initDocumentBuilder().parse(new ByteArrayInputStream(xmlData))); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Result.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Result.java index 8a61acafe77..46e99f134df 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Result.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Result.java @@ -24,33 +24,28 @@ */ package jdk.jpackage.internal.util; -import static jdk.jpackage.internal.util.function.ExceptionBox.rethrowUnchecked; - import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import java.util.function.UnaryOperator; import java.util.stream.StreamSupport; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingSupplier; public record Result(Optional value, Collection errors) { public Result { if (value.isEmpty() == errors.isEmpty()) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("'value' and 'errors' cannot both be non-empty or both be empty"); } - - if (value.isEmpty() && errors.isEmpty()) { - throw new IllegalArgumentException("Error collection must be non-empty"); - } - } public T orElseThrow() { firstError().ifPresent(ex -> { - rethrowUnchecked(ex); + throw ExceptionBox.toUnchecked(ex); }); return value.orElseThrow(); } @@ -64,7 +59,16 @@ public record Result(Optional value, Collection error } public Result map(Function conv) { - return new Result<>(value.map(conv), errors); + if (hasValue()) { + var mapped = value.map(conv); + if (mapped.isEmpty()) { + throw new NullPointerException(); + } else { + return new Result<>(mapped, errors); + } + } else { + return mapErrors(); + } } public Result flatMap(Function> conv) { @@ -73,12 +77,12 @@ public record Result(Optional value, Collection error }); } - public Result mapErrors(UnaryOperator> errorsMapper) { - return new Result<>(value, errorsMapper.apply(errors)); - } - + @SuppressWarnings("unchecked") public Result mapErrors() { - return new Result<>(Optional.empty(), errors); + if (hasValue()) { + throw new IllegalStateException("Can not map errors from a result without errors"); + } + return (Result)this; } public Result peekErrors(Consumer> consumer) { @@ -97,12 +101,31 @@ public record Result(Optional value, Collection error return errors.stream().findFirst(); } - public static Result create(Supplier supplier) { + public static Result of(Supplier supplier) { + return of(supplier::get, RuntimeException.class); + } + + public static Result of( + ThrowingSupplier supplier, Class supplierExceptionType) { + + Objects.requireNonNull(supplier); + Objects.requireNonNull(supplierExceptionType); + + T value; try { - return ofValue(supplier.get()); + value = supplier.get(); } catch (Exception ex) { - return ofError(ex); + if (supplierExceptionType.isInstance(ex)) { + return ofError(ex); + } else if (ex instanceof RuntimeException rex) { + throw rex; + } else { + // Unreachable because the `supplier` can throw exceptions of type or supertype `E` or runtime exceptions. + throw ExceptionBox.reachedUnreachable(); + } } + + return ofValue(value); } public static Result ofValue(T value) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RetryExecutor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RetryExecutor.java new file mode 100644 index 00000000000..c67373e8233 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RetryExecutor.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.util; + +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; + +import java.time.Duration; +import java.util.Iterator; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Function; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingFunction; +import jdk.jpackage.internal.util.function.ThrowingSupplier; + +public class RetryExecutor { + + public RetryExecutor(Class exceptionType) { + this.exceptionType = Objects.requireNonNull(exceptionType); + setMaxAttemptsCount(5); + setAttemptTimeout(2, TimeUnit.SECONDS); + } + + final public Class exceptionType() { + return exceptionType; + } + + public RetryExecutor setExecutable(ThrowingFunction>, T, E> v) { + executable = v; + return this; + } + + final public RetryExecutor setExecutable(ThrowingSupplier v) { + if (v != null) { + setExecutable(_ -> { + return v.get(); + }); + } else { + executable = null; + } + return this; + } + + public RetryExecutor setMaxAttemptsCount(int v) { + attempts = v; + return this; + } + + final public RetryExecutor setAttemptTimeout(long v, TimeUnit unit) { + return setAttemptTimeout(Duration.of(v, unit.toChronoUnit())); + } + + public RetryExecutor setAttemptTimeout(Duration v) { + timeout = v; + return this; + } + + public RetryExecutor setExceptionMapper(Function v) { + toUnchecked = v; + return this; + } + + public RetryExecutor setSleepFunction(Consumer v) { + sleepFunction = v; + return this; + } + + final public RetryExecutor mutate(Consumer> mutator) { + mutator.accept(this); + return this; + } + + public T execute() throws E { + var curExecutable = executable(); + T result = null; + var attemptIter = new DefaultContext(); + while (attemptIter.hasNext()) { + attemptIter.next(); + try { + result = curExecutable.apply(attemptIter); + break; + } catch (Exception ex) { + if (!exceptionType.isInstance(ex)) { + throw ExceptionBox.toUnchecked(ex); + } else if (attemptIter.isLastAttempt()) { + // No more attempts left. This is fatal. + throw exceptionType.cast(ex); + } else { + curExecutable = executable(); + } + } + + sleep(); + } + + return result; + } + + final public T executeUnchecked() { + try { + return execute(); + } catch (Error | RuntimeException t) { + throw t; + } catch (Exception ex) { + if (exceptionType.isInstance(ex)) { + throw Optional.ofNullable(toUnchecked).orElse(ExceptionBox::toUnchecked).apply(exceptionType.cast(ex)); + } else { + // Unreachable unless it is a direct subclass of Throwable, + // which is not Error or Exception which should not happen. + throw ExceptionBox.reachedUnreachable(); + } + } + } + + public interface Context { + boolean isLastAttempt(); + int attempt(); + T executor(); + } + + private final class DefaultContext implements Context>, Iterator { + + @Override + public boolean isLastAttempt() { + return !hasNext(); + } + + @Override + public int attempt() { + return attempt; + } + + @Override + public boolean hasNext() { + return (attempts - attempt) > 1; + } + + @Override + public Void next() { + attempt++; + return null; + } + + @Override + public RetryExecutor executor() { + return RetryExecutor.this; + } + + private int attempt = -1; + } + + private ThrowingFunction>, T, E> executable() { + return Optional.ofNullable(executable).orElseThrow(() -> { + return new IllegalStateException("No executable"); + }); + } + + private void sleep() { + Optional.ofNullable(timeout).ifPresent(Optional.ofNullable(sleepFunction).orElseGet(() -> { + return toConsumer(Thread::sleep); + })); + } + + private final Class exceptionType; + private ThrowingFunction>, T, E> executable; + private int attempts; + private Duration timeout; + private Function toUnchecked; + private Consumer sleepFunction; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RootedPath.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RootedPath.java new file mode 100644 index 00000000000..21d3b6b17b0 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RootedPath.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.util; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.CopyOption; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +/** + * A relative path (branch) rooted at the root. + */ +public sealed interface RootedPath { + + Path root(); + Path branch(); + + default Path fullPath() { + return root().resolve(branch()); + } + + public static Function toRootedPath(Path root) { + return path -> { + if (!path.startsWith(root)) { + throw new IllegalArgumentException(String.format("Expected path [%s] to start with [%s] root", path, root)); + } + return new Details.DefaultRootedPath(root, root.relativize(path), Files.isDirectory(path)); + }; + } + + public static void copy(Stream rootedPaths, Path dest, CopyOption...options) throws IOException { + Objects.requireNonNull(rootedPaths); + Objects.requireNonNull(dest); + + var marks = new HashMap(); + + // Preset marks for the preexisting paths. + Files.walkFileTree(dest, new FileVisitor() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + marks.put(dir, new Details.PathMark(true, true)); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + marks.put(file, new Details.PathMark(false, true)); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + return FileVisitResult.TERMINATE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + }); + + var replacePreexisting = Set.of(options).contains(StandardCopyOption.REPLACE_EXISTING); + + Predicate canReplace = v -> { + return v.isPreexisting() && replacePreexisting; + }; + + try { + rootedPaths.sequential().map(Details.DefaultRootedPath.class::cast).forEach(rootedPath -> { + + final var dstPath = dest.resolve(rootedPath.branch()); + + var dstPathMark = marks.get(dstPath); + + if (!Optional.ofNullable(dstPathMark).map(canReplace::test).orElse(true)) { + // Destination path can not be replaced, bail out. + return; + } + + // Check the ancestors of the destination path. + for (var ancestor : Details.ancestorPaths(rootedPath.branch())) { + var mark = Optional.ofNullable(marks.get(dest.resolve(ancestor))); + + if (!mark.map(Details.PathMark::isDirectory).orElse(true)) { + // `ancestor` is a file, don't overwrite it. + return; + } + } + + dstPathMark = rootedPath.createPathMark(); + marks.put(dstPath, dstPathMark); + + try { + if (replacePreexisting && (rootedPath.isDirectory() != dstPathMark.isDirectory())) { + FileUtils.deleteRecursive(dstPath); + } + + if (rootedPath.isDirectory()) { + Files.createDirectories(dstPath); + } else { + Files.createDirectories(dstPath.getParent()); + Files.copy(rootedPath.fullPath(), dstPath, options); + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + + static final class Details { + + private Details() { + } + + private record DefaultRootedPath(Path root, Path branch, boolean isDirectory) implements RootedPath { + + DefaultRootedPath { + Objects.requireNonNull(root); + Objects.requireNonNull(branch); + if (branch.isAbsolute()) { + throw new IllegalArgumentException(); + } + } + + PathMark createPathMark() { + return new PathMark(isDirectory); + } + } + + private record PathMark(boolean isDirectory, boolean isPreexisting) { + PathMark(boolean isDirectory) { + this(isDirectory, false); + } + } + + private static List ancestorPaths(Path path) { + var ancestors = new ArrayList(); + while ((path = path.getParent()) != null) { + ancestors.add(path); + } + return ancestors; + } + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/SetBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/SetBuilder.java index 18736b4ed7e..d3d677cba50 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/SetBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/SetBuilder.java @@ -31,7 +31,7 @@ import java.util.Set; public final class SetBuilder { - public static SetBuilder build(Class type) { + public static SetBuilder build() { return new SetBuilder<>(); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TeeOutputStream.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TeeOutputStream.java new file mode 100644 index 00000000000..db098eb2b48 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TeeOutputStream.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.util; + +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Objects; +import jdk.jpackage.internal.util.function.ThrowingConsumer; + +public final class TeeOutputStream extends OutputStream { + + public TeeOutputStream(Iterable items) { + items.forEach(Objects::requireNonNull); + this.items = items; + } + + @Override + public void write(int b) throws IOException { + for (final var item : items) { + item.write(b); + } + } + + @Override + public void write(byte[] b) throws IOException { + for (final var item : items) { + item.write(b); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + for (final var item : items) { + item.write(b, off, len); + } + } + + @Override + public void flush() throws IOException { + forEach(Flushable::flush); + } + + @Override + public void close() throws IOException { + forEach(Closeable::close); + } + + private void forEach(ThrowingConsumer c) throws IOException { + IOException firstEx = null; + for (final var item : items) { + try { + c.accept(item); + } catch (IOException e) { + if (firstEx == null) { + firstEx = e; + } + } + } + if (firstEx != null) { + throw firstEx; + } + } + + private final Iterable items; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/XmlUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/XmlUtils.java index c8761259254..f62ffee9260 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/XmlUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/XmlUtils.java @@ -24,7 +24,7 @@ */ package jdk.jpackage.internal.util; -import static jdk.jpackage.internal.util.function.ExceptionBox.rethrowUnchecked; +import static jdk.jpackage.internal.util.function.ExceptionBox.toUnchecked; import java.io.IOException; import java.io.Writer; @@ -80,7 +80,7 @@ public final class XmlUtils { xml.flush(); xml.close(); } catch (XMLStreamException ex) { - throw rethrowUnchecked(ex); + throw toUnchecked(ex); } } @@ -101,7 +101,7 @@ public final class XmlUtils { xml.flush(); xml.close(); } catch (XMLStreamException ex) { - throw rethrowUnchecked(ex); + throw toUnchecked(ex); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ExceptionBox.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ExceptionBox.java index 8428d5d1e7f..40503469873 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ExceptionBox.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ExceptionBox.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,23 +30,55 @@ public class ExceptionBox extends RuntimeException { private static final long serialVersionUID = 1L; - public static RuntimeException rethrowUnchecked(Throwable throwable) { - if (throwable instanceof RuntimeException err) { - throw err; + public static RuntimeException toUnchecked(Exception ex) { + switch (ex) { + case RuntimeException rex -> { + return rex; + } + case InvocationTargetException itex -> { + var t = itex.getCause(); + if (t instanceof Exception cause) { + return toUnchecked(cause); + } else { + throw (Error)t; + } + } + case InterruptedException _ -> { + Thread.currentThread().interrupt(); + return new ExceptionBox(ex); + } + default -> { + return new ExceptionBox(ex); + } } - - if (throwable instanceof Error err) { - throw err; - } - - if (throwable instanceof InvocationTargetException err) { - throw rethrowUnchecked(err.getCause()); - } - - throw new ExceptionBox(throwable); } - private ExceptionBox(Throwable throwable) { - super(throwable); + public static Exception unbox(Throwable t) { + switch (t) { + case ExceptionBox ex -> { + return unbox(ex.getCause()); + } + case InvocationTargetException ex -> { + return unbox(ex.getCause()); + } + case Exception ex -> { + return ex; + } + case Error err -> { + throw err; + } + default -> { + // Unreachable + throw reachedUnreachable(); + } + } + } + + public static Error reachedUnreachable() { + return new AssertionError("Reached unreachable!"); + } + + private ExceptionBox(Exception ex) { + super(ex); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingBiConsumer.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingBiConsumer.java index 3ed0fd67d84..374eaf803d9 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingBiConsumer.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingBiConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,17 +27,17 @@ package jdk.jpackage.internal.util.function; import java.util.function.BiConsumer; @FunctionalInterface -public interface ThrowingBiConsumer { +public interface ThrowingBiConsumer { - void accept(T t, U u) throws Throwable; + void accept(T t, U u) throws E; public static BiConsumer toBiConsumer( - ThrowingBiConsumer v) { + ThrowingBiConsumer v) { return (t, u) -> { try { v.accept(t, u); - } catch (Throwable ex) { - throw ExceptionBox.rethrowUnchecked(ex); + } catch (Exception ex) { + throw ExceptionBox.toUnchecked(ex); } }; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingBiFunction.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingBiFunction.java index 8c2df773eb5..13b0b53c887 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingBiFunction.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingBiFunction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,17 +27,17 @@ package jdk.jpackage.internal.util.function; import java.util.function.BiFunction; @FunctionalInterface -public interface ThrowingBiFunction { +public interface ThrowingBiFunction { - R apply(T t, U u) throws Throwable; + R apply(T t, U u) throws E; public static BiFunction toBiFunction( - ThrowingBiFunction v) { + ThrowingBiFunction v) { return (t, u) -> { try { return v.apply(t, u); - } catch (Throwable ex) { - throw ExceptionBox.rethrowUnchecked(ex); + } catch (Exception ex) { + throw ExceptionBox.toUnchecked(ex); } }; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingConsumer.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingConsumer.java index ef1b0a61df7..38342a361c0 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingConsumer.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,16 +27,16 @@ package jdk.jpackage.internal.util.function; import java.util.function.Consumer; @FunctionalInterface -public interface ThrowingConsumer { +public interface ThrowingConsumer { - void accept(T t) throws Throwable; + void accept(T t) throws E; - public static Consumer toConsumer(ThrowingConsumer v) { + public static Consumer toConsumer(ThrowingConsumer v) { return o -> { try { v.accept(o); - } catch (Throwable ex) { - throw ExceptionBox.rethrowUnchecked(ex); + } catch (Exception ex) { + throw ExceptionBox.toUnchecked(ex); } }; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingFunction.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingFunction.java index 2b5eae43842..5bbfb254858 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingFunction.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingFunction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,16 +27,16 @@ package jdk.jpackage.internal.util.function; import java.util.function.Function; @FunctionalInterface -public interface ThrowingFunction { +public interface ThrowingFunction { - R apply(T t) throws Throwable; + R apply(T t) throws E; - public static Function toFunction(ThrowingFunction v) { + public static Function toFunction(ThrowingFunction v) { return t -> { try { return v.apply(t); - } catch (Throwable ex) { - throw ExceptionBox.rethrowUnchecked(ex); + } catch (Exception ex) { + throw ExceptionBox.toUnchecked(ex); } }; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingRunnable.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingRunnable.java index 7c75c4d9753..f3ce5affbbf 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingRunnable.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingRunnable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,16 +25,16 @@ package jdk.jpackage.internal.util.function; @FunctionalInterface -public interface ThrowingRunnable { +public interface ThrowingRunnable { - void run() throws Throwable; + void run() throws E; - public static Runnable toRunnable(ThrowingRunnable v) { + public static Runnable toRunnable(ThrowingRunnable v) { return () -> { try { v.run(); - } catch (Throwable ex) { - throw ExceptionBox.rethrowUnchecked(ex); + } catch (Exception ex) { + throw ExceptionBox.toUnchecked(ex); } }; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingSupplier.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingSupplier.java index c69c4729190..4f194a0f4d2 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingSupplier.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,16 +27,16 @@ package jdk.jpackage.internal.util.function; import java.util.function.Supplier; @FunctionalInterface -public interface ThrowingSupplier { +public interface ThrowingSupplier { - T get() throws Throwable; + T get() throws E; - public static Supplier toSupplier(ThrowingSupplier v) { + public static Supplier toSupplier(ThrowingSupplier v) { return () -> { try { return v.get(); - } catch (Throwable ex) { - throw ExceptionBox.rethrowUnchecked(ex); + } catch (Exception ex) { + throw ExceptionBox.toUnchecked(ex); } }; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingUnaryOperator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingUnaryOperator.java index 7a2a0fd67cf..3d757bbec48 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingUnaryOperator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ThrowingUnaryOperator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,17 +27,17 @@ package jdk.jpackage.internal.util.function; import java.util.function.UnaryOperator; @FunctionalInterface -public interface ThrowingUnaryOperator { +public interface ThrowingUnaryOperator { - T apply(T t) throws Throwable; + T apply(T t) throws E; public static UnaryOperator toUnaryOperator( - ThrowingUnaryOperator v) { + ThrowingUnaryOperator v) { return t -> { try { return v.apply(t); - } catch (Throwable ex) { - throw ExceptionBox.rethrowUnchecked(ex); + } catch (Exception ex) { + throw ExceptionBox.toUnchecked(ex); } }; } diff --git a/src/jdk.jpackage/share/native/applauncher/PackageFile.cpp b/src/jdk.jpackage/share/native/applauncher/PackageFile.cpp index 31b431215d5..4bf51348d6d 100644 --- a/src/jdk.jpackage/share/native/applauncher/PackageFile.cpp +++ b/src/jdk.jpackage/share/native/applauncher/PackageFile.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jpackage/share/native/common/Dll.h b/src/jdk.jpackage/share/native/common/Dll.h index c84242373a7..bfe9e25238e 100644 --- a/src/jdk.jpackage/share/native/common/Dll.h +++ b/src/jdk.jpackage/share/native/common/Dll.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jpackage/share/native/common/app.cpp b/src/jdk.jpackage/share/native/common/app.cpp index d9492785f06..a64e84b70fb 100644 --- a/src/jdk.jpackage/share/native/common/app.cpp +++ b/src/jdk.jpackage/share/native/common/app.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jpackage/share/native/common/tstrings.cpp b/src/jdk.jpackage/share/native/common/tstrings.cpp index b38c811e8bd..7ceb87edfa1 100644 --- a/src/jdk.jpackage/share/native/common/tstrings.cpp +++ b/src/jdk.jpackage/share/native/common/tstrings.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java index e1e02ba7850..b196a5805a0 100644 --- a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java +++ b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,8 +24,6 @@ */ package jdk.jpackage.internal; -import jdk.jpackage.internal.model.Launcher; -import jdk.jpackage.internal.model.Application; import java.io.IOException; import java.nio.file.Path; import java.util.Collections; @@ -36,6 +34,9 @@ import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.jpackage.internal.model.Application; +import jdk.jpackage.internal.model.Launcher; +import jdk.jpackage.internal.util.Enquoter; /** * Helper to install launchers as services for Unix installers. diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java index 38547999290..de52a222d7d 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,29 +24,30 @@ */ package jdk.jpackage.internal; -import static jdk.jpackage.internal.WinFromOpions.createWinApplication; +import static jdk.jpackage.internal.WinFromOptions.createWinApplication; import static jdk.jpackage.internal.WinPackagingPipeline.APPLICATION_LAYOUT; import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_APP_IMAGE; import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_EXE; import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_MSI; import jdk.jpackage.internal.cli.Options; -import jdk.jpackage.internal.util.Result; public class WinBundlingEnvironment extends DefaultBundlingEnvironment { public WinBundlingEnvironment() { - super(build() - .defaultOperation(CREATE_WIN_EXE) - .bundler(CREATE_WIN_APP_IMAGE, WinBundlingEnvironment::createAppImage) - .bundler(CREATE_WIN_EXE, LazyLoad::sysEnv, WinBundlingEnvironment::createExePackage) - .bundler(CREATE_WIN_MSI, LazyLoad::sysEnv, WinBundlingEnvironment::createMsiPackage)); + super(build().mutate(builder -> { + var sysEnv = runOnce(WinSystemEnvironment::create); + + builder + .bundler(CREATE_WIN_EXE, sysEnv, WinBundlingEnvironment::createExePackage) + .bundler(CREATE_WIN_MSI, sysEnv, WinBundlingEnvironment::createMsiPackage); + }).defaultOperation(CREATE_WIN_EXE).bundler(CREATE_WIN_APP_IMAGE, WinBundlingEnvironment::createAppImage)); } private static void createMsiPackage(Options options, WinSystemEnvironment sysEnv) { createNativePackage(options, - WinFromOpions::createWinMsiPackage, + WinFromOptions.createWinMsiPackage(options), buildEnv()::create, WinPackagingPipeline.build(), (env, pkg, outputDir) -> { @@ -60,7 +61,7 @@ public class WinBundlingEnvironment extends DefaultBundlingEnvironment { private static void createExePackage(Options options, WinSystemEnvironment sysEnv) { createNativePackage(options, - WinFromOpions::createWinExePackage, + WinFromOptions.createWinExePackage(options), buildEnv()::create, WinPackagingPipeline.build(), (env, pkg, outputDir) -> { @@ -98,12 +99,4 @@ public class WinBundlingEnvironment extends DefaultBundlingEnvironment { } } - private static final class LazyLoad { - - static Result sysEnv() { - return SYS_ENV; - } - - private static final Result SYS_ENV = WinSystemEnvironment.create(); - } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java index 9a13a0f954d..21f941eedf5 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,7 +52,7 @@ final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Pat @Override public void accept(PackagingPipeline.Builder pipelineBuilder) { - pipelineBuilder.excludeDirFromCopying(outputDir) + pipelineBuilder .task(ExePackageTaskID.RUN_POST_MSI_USER_SCRIPT) .action(this::runPostMsiScript) .addDependency(PackageTaskID.CREATE_PACKAGE_FILE) @@ -79,8 +79,6 @@ final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Pat private void wrapMsiInExe() throws IOException { - Log.verbose(I18N.format("message.outputting-to-location", outputDir.toAbsolutePath())); - final var msi = msi(); // Copy template msi wrapper next to msi file @@ -102,7 +100,5 @@ final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Pat Files.copy(exePath, dstExePath, StandardCopyOption.REPLACE_EXISTING); dstExePath.toFile().setExecutable(true); - - Log.verbose(I18N.format("message.output-location", outputDir.toAbsolutePath())); } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOpions.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java similarity index 99% rename from src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOpions.java rename to src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java index 6fd6d0a7b96..6009e2f6724 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOpions.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java @@ -49,7 +49,7 @@ import jdk.jpackage.internal.model.WinLauncher; import jdk.jpackage.internal.model.WinLauncherMixin; import jdk.jpackage.internal.model.WinMsiPackage; -final class WinFromOpions { +final class WinFromOptions { static WinApplication createWinApplication(Options options) { diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java index 915d034bd82..c72b14a76d5 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -162,7 +162,7 @@ final class WinMsiPackager implements Consumer { @Override public void accept(PackagingPipeline.Builder pipelineBuilder) { - pipelineBuilder.excludeDirFromCopying(outputDir) + pipelineBuilder .task(PackagingPipeline.PackageTaskID.CREATE_CONFIG_FILES) .action(this::prepareConfigFiles) .add() @@ -315,7 +315,6 @@ final class WinMsiPackager implements Consumer { private void buildPackage() throws IOException { final var msiOut = outputDir.resolve(pkg.packageFileNameWithSuffix()); - Log.verbose(I18N.format("message.generating-msi", msiOut.toAbsolutePath())); wixPipeline.buildMsi(msiOut.toAbsolutePath()); } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinSystemEnvironment.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinSystemEnvironment.java index ab0cc37b9fe..c0bea444e00 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinSystemEnvironment.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinSystemEnvironment.java @@ -34,6 +34,6 @@ record WinSystemEnvironment(WixToolset wixToolset) implements SystemEnvironment } static Result create() { - return Result.create(WixTool::createToolset).map(WinSystemEnvironment::new); + return Result.of(WixTool::createToolset).map(WinSystemEnvironment::new); } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixLauncherAsService.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixLauncherAsService.java index 833219eaa10..0755a920fd2 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixLauncherAsService.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixLauncherAsService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java index e2ed175fbf3..c4f8610312a 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -233,10 +233,10 @@ public enum WixTool { // Detect FIPS mode var fips = false; try { - final var exec = Executor.of(toolPath.toString(), "-?").setQuiet(true).saveOutput(true); - final var exitCode = exec.execute(); + final var result = Executor.of(toolPath.toString(), "-?").setQuiet(true).saveOutput(true).execute(); + final var exitCode = result.getExitCode(); if (exitCode != 0 /* 308 */) { - final var output = exec.getOutput(); + final var output = result.getOutput(); if (!output.isEmpty() && output.get(0).contains("error CNDL0308")) { fips = true; } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinApplicationMixin.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinApplicationMixin.java index 283b5d84ae3..fde29980599 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinApplicationMixin.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinApplicationMixin.java @@ -30,7 +30,15 @@ public interface WinApplicationMixin { record Stub(DottedVersion winVersion) implements WinApplicationMixin { public Stub(Application app) { - this(DottedVersion.greedy(app.version())); + this(parse(app.version())); + } + + private static DottedVersion parse(String strVersion) { + try { + return DottedVersion.greedy(strVersion); + } catch (IllegalArgumentException ex) { + throw new JPackageException(ex.getMessage(), ex); + } } } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties index 1f485e6c6c8..38d0bd02bbb 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -56,13 +56,9 @@ error.missing-service-installer.advice=Add 'service-installer.exe' service insta message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". -message.outputting-to-location=Generating EXE for installer to: {0}. -message.output-location=Installer (.exe) saved to: {0} message.tool-version=Detected [{0}] version [{1}]. message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action. message.product-code=MSI ProductCode: {0}. message.upgrade-code=MSI UpgradeCode: {0}. message.preparing-msi-config=Preparing MSI config: {0}. -message.generating-msi=Generating MSI: {0}. - diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties index 3e6f8e30d6a..baaba64b398 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties @@ -23,15 +23,9 @@ # questions. # # - -app.bundler.name=Windows-Anwendungsimage -exe.bundler.name=EXE-Installationsprogrammpackage -msi.bundler.name=MSI-Installationsprogrammpackage - param.menu-group.default=Unbekannt resource.executable-properties-template=Vorlage für das Erstellen der ausführbaren Eigenschaftendatei -resource.setup-icon=Symbol für Dialogfeld "Setup" resource.post-msi-script=Auszuführendes Skript nach dem Erstellen der MSI-Datei für das EXE-Installationsprogramm resource.wxl-file=WiX-Lokalisierungsdatei resource.main-wix-file=Haupt-WiX-Projektdatei @@ -65,12 +59,10 @@ message.potential.windows.defender.issue=Warnung: Windows Defender verhindert ev message.outputting-to-location=EXE für Installationsprogramm wird generiert in: {0}. message.output-location=Installationsprogramm (.exe) gespeichert in: {0} message.tool-version=[{0}]-Version [{1}] erkannt. -message.creating-association-with-null-extension=Verknüpfung mit Nullerweiterung wird erstellt. message.wrong-tool-version=[{0}]-Version {1} wurde erkannt. Erforderlich ist jedoch Version {2}. message.use-wix36-features=WiX {0} erkannt. Erweiterte Bereinigungsaktion wird aktiviert. message.product-code=MSI-ProductCode: {0}. message.upgrade-code=MSI-UpgradeCode: {0}. message.preparing-msi-config=MSI-Konfiguration wird vorbereitet: {0}. message.generating-msi=MSI wird generiert: {0}. -message.invalid.install.dir=Warnung: Ungültiges Installationsverzeichnis {0}. Installationsverzeichnis muss ein relativer Unterpfad unter dem Standardinstallationsverzeichnis wie "Programme" sein. Der Anwendungsname "{1}" wird als Standardwert verwendet. diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties index 07604dc9980..119c7532b1f 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties @@ -23,15 +23,9 @@ # questions. # # - -app.bundler.name=Windowsアプリケーション・イメージ -exe.bundler.name=EXEインストーラ・パッケージ -msi.bundler.name=MSIインストーラ・パッケージ - param.menu-group.default=不明 resource.executable-properties-template=実行可能なプロパティ・ファイル作成用のテンプレート -resource.setup-icon=設定ダイアログ・アイコン resource.post-msi-script=exeインストーラのmsiファイルが作成された後に実行するスクリプト resource.wxl-file=WiXローカリゼーション・ファイル resource.main-wix-file=メインWiXプロジェクト・ファイル @@ -65,12 +59,10 @@ message.potential.windows.defender.issue=警告: Windows Defenderが原因でjpa message.outputting-to-location=インストーラのEXEを次に生成しています: {0} message.output-location=インストーラ(.exe)は次に保存されました: {0} message.tool-version=[{0}]バージョン[{1}]が検出されました。 -message.creating-association-with-null-extension=null拡張子との関連付けを作成しています。 message.wrong-tool-version=[{0}]バージョン{1}が検出されましたが、バージョン{2}が必要です。 message.use-wix36-features=WiX {0}が検出されました。拡張クリーンアップ・アクションを有効化しています。 message.product-code=MSI ProductCode: {0}。 message.upgrade-code=MSI UpgradeCode: {0}。 message.preparing-msi-config=MSI構成を準備しています: {0} message.generating-msi=MSIを生成しています: {0}。 -message.invalid.install.dir=警告: インストール・ディレクトリ{0}が無効です。インストール・ディレクトリはデフォルトのインストール場所("プログラム・ファイル"など)の下の相対サブパスである必要があります。アプリケーション名"{1}"にデフォルト設定されています。 diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties index 7eae69fba2f..66d8a9d8b96 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties @@ -23,15 +23,9 @@ # questions. # # - -app.bundler.name=Windows 应用程序映像 -exe.bundler.name=EXE 安装程序包 -msi.bundler.name=MSI 安装程序包 - param.menu-group.default=未知 resource.executable-properties-template=用于创建可执行属性文件的模板 -resource.setup-icon=设置对话框图标 resource.post-msi-script=在为 exe 安装程序创建 msi 文件之后要运行的脚本 resource.wxl-file=WiX 本地化文件 resource.main-wix-file=主 WiX 项目文件 @@ -65,12 +59,10 @@ message.potential.windows.defender.issue=警告:Windows Defender 可能会阻 message.outputting-to-location=正在为安装程序生成 EXE, 位置: {0}。 message.output-location=安装程序 (.exe) 已保存到: {0} message.tool-version=检测到 [{0}] 版本 [{1}]。 -message.creating-association-with-null-extension=正在使用空扩展名创建关联。 message.wrong-tool-version=检测到 [{0}] 版本 {1}, 但需要版本 {2}。 message.use-wix36-features=检测到 WiX {0}。正在启用高级清除操作。 message.product-code=MSI ProductCode:{0}。 message.upgrade-code=MSI UpgradeCode:{0}。 message.preparing-msi-config=正在准备 MSI 配置: {0}。 message.generating-msi=正在生成 MSI: {0}。 -message.invalid.install.dir=警告:安装目录 {0} 无效。安装目录应当是默认安装位置(如 "Program Files")下面的相对子路径。默认为应用程序名称 "{1}"。 diff --git a/src/jdk.jpackage/windows/native/common/MsiUtils.h b/src/jdk.jpackage/windows/native/common/MsiUtils.h index 26fa9f91045..d501174268d 100644 --- a/src/jdk.jpackage/windows/native/common/MsiUtils.h +++ b/src/jdk.jpackage/windows/native/common/MsiUtils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jpackage/windows/native/libjpackage/VersionInfo.cpp b/src/jdk.jpackage/windows/native/libjpackage/VersionInfo.cpp index 122757b3ac3..aa977be8a82 100644 --- a/src/jdk.jpackage/windows/native/libjpackage/VersionInfo.cpp +++ b/src/jdk.jpackage/windows/native/libjpackage/VersionInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jpackage/windows/native/libmsica/Version.cpp b/src/jdk.jpackage/windows/native/libmsica/Version.cpp index 293f2dd36be..f8c570a1349 100644 --- a/src/jdk.jpackage/windows/native/libmsica/Version.cpp +++ b/src/jdk.jpackage/windows/native/libmsica/Version.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jpackage/windows/native/libmsica/Version.h b/src/jdk.jpackage/windows/native/libmsica/Version.h index 4ef176d382f..6f91a36765e 100644 --- a/src/jdk.jpackage/windows/native/libmsica/Version.h +++ b/src/jdk.jpackage/windows/native/libmsica/Version.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jpackage/windows/native/libmsica/libmsica.cpp b/src/jdk.jpackage/windows/native/libmsica/libmsica.cpp index edfea6dc136..f338c8ab103 100644 --- a/src/jdk.jpackage/windows/native/libmsica/libmsica.cpp +++ b/src/jdk.jpackage/windows/native/libmsica/libmsica.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/IOContext.java b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/IOContext.java index e6d43bb346e..d662b62c157 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/IOContext.java +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/IOContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java index 795321253fd..9a030f7e46b 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java @@ -3362,7 +3362,19 @@ public class JShellTool implements MessageHandler { // error occurred, already reported return false; } - try (BufferedWriter writer = Files.newBufferedWriter(toPathResolvingUserHome(filename), + // Create missing parent directories before writing to target file + Path target; + try { + target = toPathResolvingUserHome(filename); + Path parent = target.getParent(); + if (parent != null) { + Files.createDirectories(parent); + } + } catch (Exception e) { + errormsg("jshell.err.file.exception", "/save", filename, e); + return false; + } + try (BufferedWriter writer = Files.newBufferedWriter(target, Charset.defaultCharset(), CREATE, TRUNCATE_EXISTING, WRITE)) { if (at.hasOption("-history")) { diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Startup.java b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Startup.java index f2472ee5623..9925a0141f1 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Startup.java +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Startup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties index 351a0f812ad..e9918a7f628 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_de.properties b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_de.properties index cb93ae4ed57..744e71db667 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_de.properties +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_de.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_ja.properties b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_ja.properties index fa84c3f38fb..becf6854a74 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_ja.properties +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_ja.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_zh_CN.properties b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_zh_CN.properties index 879e9633e6a..c175db36da6 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_zh_CN.properties +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n_zh_CN.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jshell/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java b/src/jdk.jshell/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java index 099aba26968..7a1dd6c9877 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java +++ b/src/jdk.jshell/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jshell/share/classes/jdk/jshell/OuterWrapMap.java b/src/jdk.jshell/share/classes/jdk/jshell/OuterWrapMap.java index 61fb3bdfc9a..ca1d7cc8fd3 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/OuterWrapMap.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/OuterWrapMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java b/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java index 992d97a7040..dc82d1d06a1 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java index 526580988ab..1df6dc5191b 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/jdk.management/share/classes/com/sun/management/DiagnosticCommandMBean.java b/src/jdk.management/share/classes/com/sun/management/DiagnosticCommandMBean.java index d5fedf93855..994757f17c3 100644 --- a/src/jdk.management/share/classes/com/sun/management/DiagnosticCommandMBean.java +++ b/src/jdk.management/share/classes/com/sun/management/DiagnosticCommandMBean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -135,7 +135,7 @@ import javax.management.DynamicMBean; * * * dcmd.enabledboolean - * True if the diagnostic command is enabled, false otherwise + * This field is always true -- diagnostic commands cannot be disabled. * * * dcmd.argumentsDescriptor diff --git a/src/jdk.management/share/classes/com/sun/management/internal/HotSpotAOTCacheImpl.java b/src/jdk.management/share/classes/com/sun/management/internal/HotSpotAOTCacheImpl.java new file mode 100644 index 00000000000..4bb556455ea --- /dev/null +++ b/src/jdk.management/share/classes/com/sun/management/internal/HotSpotAOTCacheImpl.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025, Microsoft, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.management.internal; + +import javax.management.ObjectName; +import jdk.management.HotSpotAOTCacheMXBean; +import sun.management.Util; +import sun.management.VMManagement; + +/** + * Implementation class for the AOT Cache subsystem. + * + * ManagementFactory.getRuntimeMXBean() returns an instance + * of this class. + */ +public class HotSpotAOTCacheImpl implements HotSpotAOTCacheMXBean { + + private final VMManagement jvm; + /** + * Constructor of HotSpotAOTCacheImpl class. + */ + HotSpotAOTCacheImpl(VMManagement vm) { + this.jvm = vm; + } + + public boolean endRecording() { + return jvm.endAOTRecording(); + } + + public ObjectName getObjectName() { + return Util.newObjectName("jdk.management:type=HotSpotAOTCache"); + } +} \ No newline at end of file diff --git a/src/jdk.management/share/classes/com/sun/management/internal/PlatformMBeanProviderImpl.java b/src/jdk.management/share/classes/com/sun/management/internal/PlatformMBeanProviderImpl.java index 3a64fe6b858..b000516e626 100644 --- a/src/jdk.management/share/classes/com/sun/management/internal/PlatformMBeanProviderImpl.java +++ b/src/jdk.management/share/classes/com/sun/management/internal/PlatformMBeanProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,7 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.management.DynamicMBean; +import jdk.management.HotSpotAOTCacheMXBean; import jdk.management.VirtualThreadSchedulerMXBean; import sun.management.ManagementFactoryHelper; import sun.management.spi.PlatformMBeanProvider; @@ -159,6 +160,41 @@ public final class PlatformMBeanProviderImpl extends PlatformMBeanProvider { } }); + /** + * HotSpotAOTCacheMXBean. + */ + initMBeanList.add(new PlatformComponent() { + private final Set> mbeanInterfaces = + Set.of(HotSpotAOTCacheMXBean.class); + private final Set mbeanInterfaceNames = + Set.of(HotSpotAOTCacheMXBean.class.getName()); + private HotSpotAOTCacheMXBean impl; + + @Override + public Set> mbeanInterfaces() { + return mbeanInterfaces; + } + + @Override + public Set mbeanInterfaceNames() { + return mbeanInterfaceNames; + } + + @Override + public String getObjectNamePattern() { + return "jdk.management:type=HotSpotAOTCache"; + } + + @Override + public Map nameToMBeanMap() { + HotSpotAOTCacheMXBean impl = this.impl; + if (impl == null) { + this.impl = impl = new HotSpotAOTCacheImpl(ManagementFactoryHelper.getVMManagement()); + } + return Map.of("jdk.management:type=HotSpotAOTCache", impl); + } + }); + /** * VirtualThreadSchedulerMXBean. */ diff --git a/src/jdk.management/share/classes/jdk/management/HotSpotAOTCacheMXBean.java b/src/jdk.management/share/classes/jdk/management/HotSpotAOTCacheMXBean.java new file mode 100644 index 00000000000..f8e1e5a7f05 --- /dev/null +++ b/src/jdk.management/share/classes/jdk/management/HotSpotAOTCacheMXBean.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2025, Microsoft, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.management; + +import java.lang.management.ManagementFactory; +import java.lang.management.PlatformManagedObject; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +/** + * Management interface for the JDK's Ahead of Time (AOT) Cache. + * + *

The management interface is registered with the platform {@link MBeanServer + * MBeanServer}. The {@link ObjectName ObjectName} that uniquely identifies the management + * interface within the {@code MBeanServer} is {@code jdk.management:type=HotSpotAOTCache}. + * + *

Direct access to the MXBean interface can be obtained with + * {@link ManagementFactory#getPlatformMXBean(Class)}. + * + * @since 26 + */ +public interface HotSpotAOTCacheMXBean extends PlatformManagedObject { + /** + * If an AOT recording is in progress, ends the recording. This method returns + * after the AOT artifacts have been completely written. + * + *

The JVM will start recording AOT artifacts upon start-up if appropriate JVM options are + * given in the command-line. The recording will stop when the JVM exits, or when + * the {@code endRecording} method is called. Examples: + * + *

${@code java -XX:AOTCacheOutput=app.aot ....} + * + *

+ * The JVM records optimization information for the current application in the AOT cache file + * {@code app.aot}. In a future run of the application, the option {@code -XX:AOTCache=app.aot} will + * cause the JVM to use the cache to improve the application's startup and warmup performance. + *
+ * + *

${@code java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconfig ....} + * + *

+ * The JVM records optimization information for the current application in the AOT configuration + * file {@code app.aotconfig}. Subsequently, an AOT cache file can be created with the command: + * + *

${@code java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconfig -XX:AOTCache=app.aot ...} + *

+ * + *

For more information about creating and using the AOT artifacts, and detailed + * specification of the corresponding JVM command-line options, please refer + * to JEP 483 and JEP 514. + * + *

Currently there are no APIs to start an AOT recording. AOT recordings must be + * started using JVM command-line options such as {@code -XX:AOTCacheOutput}. + * There are also no APIs to query whether an AOT recording is in progress, or what AOT + * artifacts are being recorded. + * + *

This method enables an application to end its own AOT recording + * programatically, but that is not necessarily the best approach. Doing so + * requires changing the application’s code, which might not be + * feasible. Even when it is feasible, injecting training-specific logic + * into the application reduces the similarity between training runs and + * production runs, potentially making the AOT cache less effective. It may + * be better to arrange for an external agent to end the training run, + * thereby creating an AOT cache without interfering with the application’s + * code. + * + * @return {@code true} if a recording was in progress and has been ended + * successfully; {@code false} otherwise. + */ + public boolean endRecording(); +} diff --git a/src/jdk.management/share/classes/jdk/management/VirtualThreadSchedulerMXBean.java b/src/jdk.management/share/classes/jdk/management/VirtualThreadSchedulerMXBean.java index 556c5184ffe..358ab23ca93 100644 --- a/src/jdk.management/share/classes/jdk/management/VirtualThreadSchedulerMXBean.java +++ b/src/jdk.management/share/classes/jdk/management/VirtualThreadSchedulerMXBean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,7 +76,7 @@ public interface VirtualThreadSchedulerMXBean extends PlatformManagedObject { * @throws IllegalArgumentException if size is less than the minimum, or * greater than the maximum, supported by the scheduler * @throws UnsupportedOperationException if changing the target - * parallelism is not suppored by the scheduler + * parallelism is not supported by the scheduler * * @see ForkJoinPool#setParallelism(int) */ diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/FreeInteractiveLayoutManager.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/FreeInteractiveLayoutManager.java index 7aec57ec2a0..ab334007ced 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/FreeInteractiveLayoutManager.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/FreeInteractiveLayoutManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java index cb9487ae828..a73d7bb6dde 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java index 956d72924b9..9c285886e71 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutMover.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutMover.java index 48599d78d75..3126a8dba9e 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutMover.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutMover.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java index 2816115080e..2ef3211a3b0 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/EnableFreeLayoutAction.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/EnableFreeLayoutAction.java index 201fc51e15e..6af8bb851e2 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/EnableFreeLayoutAction.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/EnableFreeLayoutAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java index 01ebb8ed9e3..0c9f636e373 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java index 61cbc054200..9a806a85552 100644 --- a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java +++ b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java index d230f1b4336..3a9a6c2cbef 100644 --- a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java +++ b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/GroupJavacBenchmark.java b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/GroupJavacBenchmark.java index 5ca3f0bf648..9eb6c893cc6 100644 --- a/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/GroupJavacBenchmark.java +++ b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/GroupJavacBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/JavacBenchmark.java b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/JavacBenchmark.java index 28c5c57a073..565ea4038c7 100644 --- a/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/JavacBenchmark.java +++ b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/JavacBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/SingleJavacBenchmark.java b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/SingleJavacBenchmark.java index 2cc5c1a7c83..00c5bd7ee92 100644 --- a/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/SingleJavacBenchmark.java +++ b/test/benchmarks/micros-javac/src/main/java/org/openjdk/bench/langtools/javac/SingleJavacBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/docs/ProblemList.txt b/test/docs/ProblemList.txt index 4df8bbcc53c..c846678665d 100644 --- a/test/docs/ProblemList.txt +++ b/test/docs/ProblemList.txt @@ -39,7 +39,3 @@ # More than one label is allowed but must be on the same line. # ############################################################################# - -jdk/javadoc/doccheck/checks/jdkCheckLinks.java 8370249 generic-all -jdk/javadoc/doccheck/checks/jdkCheckHtml.java 8370970 generic-all -jdk/javadoc/doccheck/checks/jdkDoctypeBadcharsCheck.java 8370970 generic-all diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.java index d8fd13fdd7c..af28135e673 100644 --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.java +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,19 +23,50 @@ package jdk.test.failurehandler; +import java.io.FileWriter; +import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Objects; public class HtmlPage implements AutoCloseable { + static final String STYLE_SHEET_FILENAME = "failure-handler-style.css"; + static final String SCRIPT_FILENAME = "failure-handler-script.js"; + private final PrintWriter writer; private final HtmlSection rootSection; - public HtmlPage(PrintWriter writer) { - Objects.requireNonNull(writer, "writer cannot be null"); - this.writer = writer; + /** + * Constructs a {@code HtmlPage} + * + * @param dir The directory into which the HTML file and related resources will be created + * @param htmlFileName The HTML file name + * @param append if {@code true} then the content will be appended to the file represented + * by the {@code htmlFileName}, else the {@code htmlFileName} will be overwritten + * with the new content + * @throws IllegalArgumentException if {@code dir} is not a directory or if the + * {@code htmlFileName} is {@linkplain String#isBlank() blank} + * @throws IOException if there is an error constructing file resource(s) for this HTML page + */ + public HtmlPage(final Path dir, final String htmlFileName, final boolean append) + throws IOException { + Objects.requireNonNull(dir, "directory cannot be null"); + Objects.requireNonNull(htmlFileName, "HTML file name cannot be null"); + if (!Files.isDirectory(dir)) { + throw new IllegalArgumentException(dir + " is not a directory"); + } + if (htmlFileName.isBlank()) { + throw new IllegalArgumentException("HTML file name cannot be blank"); + } + final FileWriter fileWriter = new FileWriter(dir.resolve(htmlFileName).toFile(), append); + this.writer = new PrintWriter(fileWriter, true); + createScriptFile(dir); + createStyleSheetFile(dir); rootSection = new HtmlSection(writer); } + @Override public void close() { writer.close(); @@ -44,4 +75,71 @@ public class HtmlPage implements AutoCloseable { public HtmlSection getRootSection() { return rootSection; } + + private static void createStyleSheetFile(final Path destDir) throws IOException { + final Path styleSheet = destDir.resolve(STYLE_SHEET_FILENAME); + if (Files.exists(styleSheet)) { + return; + } + final String content = """ + div { display:none;} + """; + Files.writeString(styleSheet, content); + } + + private static void createScriptFile(final Path destDir) throws IOException { + final Path script = destDir.resolve(SCRIPT_FILENAME); + if (Files.exists(script)) { + return; + } + final String content = """ + function doShow(e) { + while (e != null) { + if (e.tagName == 'DIV') { + e.style.display = 'block'; + } + e = e.parentNode; + } + } + + function showHandler(event) { + elementId = this.dataset.show; + elementToShow = document.getElementById(elementId); + doShow(elementToShow); + } + + function toggleHandler(event) { + toggleElementId = this.dataset.toggle; + elementToToggle = document.getElementById(toggleElementId); + d = elementToToggle.style.display; + if (d == 'block') { + elementToToggle.style.display = 'none'; + } else { + doShow(elementToToggle); + } + } + + function bodyLoadHandler() { + const index = location.href.indexOf("#"); + if (index != -1) { + doShow(document.getElementById(location.href.substring(index + 1))); + } + // elements that require the "toggleHandler" function to be registered + // as an event handler for the onclick event + const requiringToggleHandler = document.querySelectorAll("[data-toggle]"); + for (const e of requiringToggleHandler) { + e.addEventListener("click", toggleHandler); + } + // elements that require the "showHandler" function to be registered + // as an event handler for the onclick event + const requiringShowHandler = document.querySelectorAll("[data-show]"); + for (const e of requiringShowHandler) { + e.addEventListener("click", showHandler); + } + } + // register a onload event handler + window.addEventListener("DOMContentLoaded", bodyLoadHandler); + """; + Files.writeString(script, content); + } } diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.java index 141caf450bd..53ffab95a1e 100644 --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.java +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,41 +57,15 @@ public class HtmlSection { if (rootSection == null) { this.rootSection = this; this.pw.println(""); - this.pw.println("\n" - + "\n" - + "\n" - + ""); - this.pw.println(""); + this.pw.println(""); + this.pw.println( + ""); + this.pw.println( + ""); + this.pw.println(""); + + this.pw.println(""); } else { this.rootSection = rootSection; this.pw.print("

    "); @@ -146,7 +120,7 @@ public class HtmlSection { } else if (child != null) { path = String.format("%s.%s", path, child); } - pw.printf("%2$s%n", + pw.printf("%2$s%n", path, name); } @@ -188,7 +162,7 @@ public class HtmlSection { : String.format("%s.%s", parent.id, name), name, rootSection); this.parent = parent; - pw.printf("
  • %2$s
    ",
    +            pw.printf("
  • %2$s
    ",
                         id, name);
             }
     
    diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java
    index 96c5f4f2858..32dbedb5159 100644
    --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java
    +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -29,13 +29,14 @@ import com.sun.javatest.TestResult;
     import com.sun.javatest.regtest.config.RegressionParameters;
     import jdk.test.failurehandler.*;
     
    -import java.io.File;
     import java.io.FileWriter;
     import java.io.IOException;
     import java.io.PrintWriter;
     import java.nio.file.Files;
     import java.nio.file.Path;
     import java.nio.file.Paths;
    +import java.util.List;
    +import java.util.stream.Stream;
     
     /**
      * The jtreg test execution observer, which gathers info about
    @@ -85,11 +86,15 @@ public class GatherDiagnosticInfoObserver implements Harness.Observer {
                         testJdk, compileJdk);
                 gatherEnvInfo(workDir, name, log,
                         gathererFactory.getEnvironmentInfoGatherer());
    -            Files.walk(workDir)
    -                    .filter(Files::isRegularFile)
    -                    .filter(f -> (f.getFileName().toString().contains("core") || f.getFileName().toString().contains("mdmp")))
    -                    .forEach(core -> gatherCoreInfo(workDir, name,
    -                            core, log, gathererFactory.getCoreInfoGatherer()));
    +            // generate a cores.html file after parsing the core dump files (if any)
    +            List coreFiles;
    +            try (Stream paths = Files.walk(workDir)) {
    +                coreFiles = paths.filter(Files::isRegularFile)
    +                        .filter(f -> (f.getFileName().toString().contains("core")
    +                                || f.getFileName().toString().contains("mdmp")))
    +                        .toList();
    +            }
    +            gatherCoreInfo(workDir, name, coreFiles, log, gathererFactory.getCoreInfoGatherer());
             } catch (Throwable e) {
                 log.printf("ERROR: exception in observer %s:", name);
                 e.printStackTrace(log);
    @@ -103,27 +108,29 @@ public class GatherDiagnosticInfoObserver implements Harness.Observer {
             }
         }
     
    -    private void gatherCoreInfo(Path workDir, String name, Path core, PrintWriter log,
    -                               CoreInfoGatherer gatherer) {
    -        File output = workDir.resolve(CORES_OUTPUT).toFile();
    -        try (HtmlPage html = new HtmlPage(new PrintWriter(
    -                new FileWriter(output, true), true))) {
    +    private void gatherCoreInfo(Path workDir, String name, List coreFiles,
    +                                PrintWriter log, CoreInfoGatherer gatherer) {
    +        if (coreFiles.isEmpty()) {
    +            return;
    +        }
    +        try (HtmlPage html = new HtmlPage(workDir, CORES_OUTPUT, true)) {
                 try (ElapsedTimePrinter timePrinter
                              = new ElapsedTimePrinter(new Stopwatch(), name, log)) {
    -                gatherer.gatherCoreInfo(html.getRootSection(), core);
    +                // gather information from the contents of each core file
    +                for (Path coreFile : coreFiles) {
    +                    gatherer.gatherCoreInfo(html.getRootSection(), coreFile);
    +                }
                 }
             } catch (Throwable e) {
    -            log.printf("ERROR: exception in observer on getting environment "
    -                    + "information %s:", name);
    +            log.printf("ERROR: exception in %s observer while gathering information from"
    +                    + " core dump file", name);
                 e.printStackTrace(log);
             }
         }
     
         private void gatherEnvInfo(Path workDir, String name, PrintWriter log,
                                    EnvironmentInfoGatherer gatherer) {
    -        File output = workDir.resolve(ENVIRONMENT_OUTPUT).toFile();
    -        try (HtmlPage html = new HtmlPage(new PrintWriter(
    -                new FileWriter(output, true), true))) {
    +        try (HtmlPage html = new HtmlPage(workDir, ENVIRONMENT_OUTPUT, true)) {
                 try (ElapsedTimePrinter timePrinter
                              = new ElapsedTimePrinter(new Stopwatch(), name, log)) {
                     gatherer.gatherEnvironmentInfo(html.getRootSection());
    diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.java
    index 63ed7e5f2c7..c1f1ddfb69f 100644
    --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.java
    +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -69,16 +69,7 @@ public class GatherProcessInfoTimeoutHandler extends TimeoutHandler {
             }
             try {
                 actionsLog.printf("%s ---%n", name);
    -
    -            File output = workDir.resolve(OUTPUT_FILENAME).toFile();
    -            try {
    -                PrintWriter pw = new PrintWriter(new FileWriter(output, true), true);
    -                runGatherer(name, workDir, actionsLog, pw, pid);
    -            } catch (IOException e) {
    -                actionsLog.printf("IOException: cannot open output file[%s] : %s",
    -                        output, e.getMessage());
    -                e.printStackTrace(actionsLog);
    -            }
    +            runGatherer(name, actionsLog, pid);
             } finally {
                 actionsLog.printf("--- %s%n", name);
                 // don't close jtreg log
    @@ -90,9 +81,9 @@ public class GatherProcessInfoTimeoutHandler extends TimeoutHandler {
             }
         }
     
    -    private void runGatherer(String name, Path workDir, PrintWriter log,
    -                             PrintWriter out, long pid) {
    -        try (HtmlPage html = new HtmlPage(out)) {
    +    private void runGatherer(String name, PrintWriter log, long pid) {
    +        Path workDir = outputDir.toPath();
    +        try (HtmlPage html = new HtmlPage(workDir, OUTPUT_FILENAME, true)) {
                 ProcessInfoGatherer gatherer = new GathererFactory(
                         OS.current().family,
                         workDir, log, testJdk.toPath()).getProcessInfoGatherer();
    diff --git a/test/failure_handler/src/share/conf/mac.properties b/test/failure_handler/src/share/conf/mac.properties
    index e04b49e0fcf..6a4ada31e98 100644
    --- a/test/failure_handler/src/share/conf/mac.properties
    +++ b/test/failure_handler/src/share/conf/mac.properties
    @@ -1,5 +1,5 @@
     #
    -# Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
    +# Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
     # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     #
     # This code is free software; you can redistribute it and/or modify it
    diff --git a/test/hotspot/gtest/cds/test_archiveWorkers.cpp b/test/hotspot/gtest/cds/test_archiveWorkers.cpp
    index 55c3528f0c2..87e55b80b94 100644
    --- a/test/hotspot/gtest/cds/test_archiveWorkers.cpp
    +++ b/test/hotspot/gtest/cds/test_archiveWorkers.cpp
    @@ -1,4 +1,5 @@
     /*
    + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
      * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
    @@ -23,20 +24,21 @@
      */
     
     #include "cds/archiveUtils.hpp"
    +#include "runtime/atomic.hpp"
     #include "unittest.hpp"
     
     class TestArchiveWorkerTask : public ArchiveWorkerTask {
     private:
    -  volatile int _sum;
    -  int _max;
    +  Atomic _sum;
    +  Atomic _max;
     public:
       TestArchiveWorkerTask() : ArchiveWorkerTask("Test"), _sum(0), _max(0) {}
       void work(int chunk, int max_chunks) override {
    -    AtomicAccess::add(&_sum, chunk);
    -    AtomicAccess::store(&_max, max_chunks);
    +    _sum.add_then_fetch(chunk);
    +    _max.store_relaxed(max_chunks);
       }
    -  int sum() { return AtomicAccess::load(&_sum); }
    -  int max() { return AtomicAccess::load(&_max); }
    +  int sum() { return _sum.load_relaxed(); }
    +  int max() { return _max.load_relaxed(); }
     };
     
     // Test a repeated cycle of workers init/shutdown without task works.
    diff --git a/test/hotspot/gtest/gc/g1/test_g1BatchedGangTask.cpp b/test/hotspot/gtest/gc/g1/test_g1BatchedGangTask.cpp
    index 902406eaf65..08df547585a 100644
    --- a/test/hotspot/gtest/gc/g1/test_g1BatchedGangTask.cpp
    +++ b/test/hotspot/gtest/gc/g1/test_g1BatchedGangTask.cpp
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -22,9 +22,10 @@
      *
      */
     
    +#include "cppstdlib/new.hpp"
     #include "gc/g1/g1BatchedTask.hpp"
     #include "gc/shared/workerThread.hpp"
    -#include "runtime/atomicAccess.hpp"
    +#include "runtime/atomic.hpp"
     #include "unittest.hpp"
     
     class G1BatchedTaskWorkers : AllStatic {
    @@ -49,26 +50,26 @@ WorkerThreads* G1BatchedTaskWorkers::_workers = nullptr;
     
     class G1TestSubTask : public G1AbstractSubTask {
       mutable uint _phase;
    -  volatile uint _num_do_work; // Amount of do_work() has been called.
    +  Atomic _num_do_work; // Amount of do_work() has been called.
     
       void check_and_inc_phase(uint expected) const {
         ASSERT_EQ(_phase, expected);
         _phase++;
       }
     
    -  bool volatile* _do_work_called_by;
    +  Atomic* _do_work_called_by;
     
     protected:
       uint _max_workers;
     
       void do_work_called(uint worker_id) {
    -    AtomicAccess::inc(&_num_do_work);
    -    bool orig_value = AtomicAccess::cmpxchg(&_do_work_called_by[worker_id], false, true);
    +    _num_do_work.add_then_fetch(1u);
    +    bool orig_value = _do_work_called_by[worker_id].compare_exchange(false, true);
         ASSERT_EQ(orig_value, false);
       }
     
       void verify_do_work_called_by(uint num_workers) {
    -    ASSERT_EQ(AtomicAccess::load(&_num_do_work), num_workers);
    +    ASSERT_EQ(_num_do_work.load_relaxed(), num_workers);
         // Do not need to check the _do_work_called_by array. The count is already verified
         // by above statement, and we already check that a given flag is only set once.
       }
    @@ -86,7 +87,7 @@ public:
     
       ~G1TestSubTask() {
         check_and_inc_phase(3);
    -    FREE_C_HEAP_ARRAY(bool, _do_work_called_by);
    +    FREE_C_HEAP_ARRAY(Atomic, _do_work_called_by);
       }
     
       double worker_cost() const override {
    @@ -100,9 +101,9 @@ public:
         assert(max_workers >= 1, "must be");
         check_and_inc_phase(2);
     
    -    _do_work_called_by = NEW_C_HEAP_ARRAY(bool, max_workers, mtInternal);
    +    _do_work_called_by = NEW_C_HEAP_ARRAY(Atomic, max_workers, mtInternal);
         for (uint i = 0; i < max_workers; i++) {
    -      _do_work_called_by[i] = false;
    +      ::new (&_do_work_called_by[i]) Atomic{false};
         }
         _max_workers = max_workers;
       }
    diff --git a/test/hotspot/gtest/gc/g1/test_g1CardSet.cpp b/test/hotspot/gtest/gc/g1/test_g1CardSet.cpp
    index 70a70dc069f..c2de96bea4f 100644
    --- a/test/hotspot/gtest/gc/g1/test_g1CardSet.cpp
    +++ b/test/hotspot/gtest/gc/g1/test_g1CardSet.cpp
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -30,6 +30,7 @@
     #include "gc/shared/workerThread.hpp"
     #include "logging/log.hpp"
     #include "memory/allocation.hpp"
    +#include "runtime/atomic.hpp"
     #include "unittest.hpp"
     #include "utilities/powerOfTwo.hpp"
     
    @@ -385,8 +386,8 @@ void G1CardSetTest::cardset_basic_test() {
     class G1CardSetMtTestTask : public WorkerTask {
       G1CardSet* _card_set;
     
    -  size_t _added;
    -  size_t _found;
    +  Atomic _added;
    +  Atomic _found;
     
     public:
       G1CardSetMtTestTask(G1CardSet* card_set) :
    @@ -413,12 +414,12 @@ public:
             found++;
           }
         }
    -    AtomicAccess::add(&_added, added);
    -    AtomicAccess::add(&_found, found);
    +    _added.add_then_fetch(added);
    +    _found.add_then_fetch(found);
       }
     
    -  size_t added() const { return _added; }
    -  size_t found() const { return _found; }
    +  size_t added() const { return _added.load_relaxed(); }
    +  size_t found() const { return _found.load_relaxed(); }
     };
     
     void G1CardSetTest::cardset_mt_test() {
    diff --git a/test/hotspot/gtest/gc/g1/test_heapRegion.cpp b/test/hotspot/gtest/gc/g1/test_heapRegion.cpp
    index ec7722ad384..8dc609ca69e 100644
    --- a/test/hotspot/gtest/gc/g1/test_heapRegion.cpp
    +++ b/test/hotspot/gtest/gc/g1/test_heapRegion.cpp
    @@ -79,7 +79,6 @@ void VM_HeapRegionApplyToMarkedObjectsTest::doit() {
       bitmap->par_mark(region->bottom() + MARK_OFFSET_1);
       bitmap->par_mark(region->bottom() + MARK_OFFSET_2);
       bitmap->par_mark(region->bottom() + MARK_OFFSET_3);
    -  bitmap->par_mark(region->end());
     
       VerifyAndCountMarkClosure cl(bitmap);
     
    diff --git a/test/hotspot/gtest/gc/g1/test_stressCommitUncommit.cpp b/test/hotspot/gtest/gc/g1/test_stressCommitUncommit.cpp
    index 6b3e9fd50bc..ce6a4f8819e 100644
    --- a/test/hotspot/gtest/gc/g1/test_stressCommitUncommit.cpp
    +++ b/test/hotspot/gtest/gc/g1/test_stressCommitUncommit.cpp
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -26,7 +26,7 @@
     #include "gc/g1/g1RegionToSpaceMapper.hpp"
     #include "gc/shared/workerThread.hpp"
     #include "memory/memoryReserver.hpp"
    -#include "runtime/atomicAccess.hpp"
    +#include "runtime/atomic.hpp"
     #include "runtime/os.hpp"
     #include "unittest.hpp"
     
    @@ -51,7 +51,7 @@ WorkerThreads* G1MapperWorkers::_workers = nullptr;
     
     class G1TestCommitUncommit : public WorkerTask {
       G1RegionToSpaceMapper* _mapper;
    -  uint _claim_id;
    +  Atomic _claim_id;
     public:
       G1TestCommitUncommit(G1RegionToSpaceMapper* mapper) :
           WorkerTask("Stress mapper"),
    @@ -59,7 +59,7 @@ public:
           _claim_id(0) { }
     
       void work(uint worker_id) {
    -    uint index = AtomicAccess::fetch_then_add(&_claim_id, 1u);
    +    uint index = _claim_id.fetch_then_add(1u);
     
         for (int i = 0; i < 100000; i++) {
           // Stress commit and uncommit of a single region. The same
    diff --git a/test/hotspot/gtest/gc/shared/test_bufferNodeAllocator.cpp b/test/hotspot/gtest/gc/shared/test_bufferNodeAllocator.cpp
    index ec72ce404e3..edcea03d696 100644
    --- a/test/hotspot/gtest/gc/shared/test_bufferNodeAllocator.cpp
    +++ b/test/hotspot/gtest/gc/shared/test_bufferNodeAllocator.cpp
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -24,7 +24,7 @@
     
     #include "gc/shared/bufferNode.hpp"
     #include "memory/allocation.hpp"
    -#include "runtime/atomicAccess.hpp"
    +#include "runtime/atomic.hpp"
     #include "runtime/interfaceSupport.inline.hpp"
     #include "runtime/semaphore.inline.hpp"
     #include "runtime/thread.hpp"
    @@ -116,16 +116,16 @@ public:
     class BufferNode::TestSupport::AllocatorThread : public JavaTestThread {
       BufferNode::Allocator* _allocator;
       CompletedList* _cbl;
    -  volatile size_t* _total_allocations;
    -  volatile bool* _continue_running;
    +  Atomic* _total_allocations;
    +  Atomic* _continue_running;
       size_t _allocations;
     
     public:
       AllocatorThread(Semaphore* post,
                       BufferNode::Allocator* allocator,
                       CompletedList* cbl,
    -                  volatile size_t* total_allocations,
    -                  volatile bool* continue_running) :
    +                  Atomic* total_allocations,
    +                  Atomic* continue_running) :
         JavaTestThread(post),
         _allocator(allocator),
         _cbl(cbl),
    @@ -135,14 +135,14 @@ public:
       {}
     
       virtual void main_run() {
    -    while (AtomicAccess::load_acquire(_continue_running)) {
    +    while (_continue_running->load_acquire()) {
           BufferNode* node = _allocator->allocate();
           _cbl->push(node);
           ++_allocations;
           ThreadBlockInVM tbiv(this); // Safepoint check.
         }
         tty->print_cr("allocations: %zu", _allocations);
    -    AtomicAccess::add(_total_allocations, _allocations);
    +    _total_allocations->add_then_fetch(_allocations);
       }
     };
     
    @@ -151,13 +151,13 @@ public:
     class BufferNode::TestSupport::ProcessorThread : public JavaTestThread {
       BufferNode::Allocator* _allocator;
       CompletedList* _cbl;
    -  volatile bool* _continue_running;
    +  Atomic* _continue_running;
     
     public:
       ProcessorThread(Semaphore* post,
                       BufferNode::Allocator* allocator,
                       CompletedList* cbl,
    -                  volatile bool* continue_running) :
    +                  Atomic* continue_running) :
         JavaTestThread(post),
         _allocator(allocator),
         _cbl(cbl),
    @@ -172,7 +172,7 @@ public:
             _allocator->release(node);
           } else if (shutdown_requested) {
             return;
    -      } else if (!AtomicAccess::load_acquire(_continue_running)) {
    +      } else if (!_continue_running->load_acquire()) {
             // To avoid a race that could leave buffers in the list after this
             // thread has shut down, continue processing until the list is empty
             // *after* the shut down request has been received.
    @@ -193,9 +193,9 @@ static void run_test(BufferNode::Allocator* allocator, CompletedList* cbl) {
       constexpr uint milliseconds_to_run = 1000;
     
       Semaphore post;
    -  volatile size_t total_allocations = 0;
    -  volatile bool allocator_running = true;
    -  volatile bool processor_running = true;
    +  Atomic total_allocations{0};
    +  Atomic allocator_running{true};
    +  Atomic processor_running{true};
     
       ProcessorThread* proc_threads[num_processor_threads] = {};
       for (uint i = 0; i < num_processor_threads; ++i) {
    @@ -222,18 +222,18 @@ static void run_test(BufferNode::Allocator* allocator, CompletedList* cbl) {
         ThreadInVMfromNative invm(this_thread);
         this_thread->sleep(milliseconds_to_run);
       }
    -  AtomicAccess::release_store(&allocator_running, false);
    +  allocator_running.release_store(false);
       for (uint i = 0; i < num_allocator_threads; ++i) {
         ThreadInVMfromNative invm(this_thread);
         post.wait_with_safepoint_check(this_thread);
       }
    -  AtomicAccess::release_store(&processor_running, false);
    +  processor_running.release_store(false);
       for (uint i = 0; i < num_processor_threads; ++i) {
         ThreadInVMfromNative invm(this_thread);
         post.wait_with_safepoint_check(this_thread);
       }
       ASSERT_TRUE(BufferNode::TestSupport::try_transfer_pending(allocator));
    -  tty->print_cr("total allocations: %zu", total_allocations);
    +  tty->print_cr("total allocations: %zu", total_allocations.load_relaxed());
       tty->print_cr("allocator free count: %zu", allocator->free_count());
     }
     
    diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp
    index b2491226e5c..4167e33b706 100644
    --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp
    +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp
    @@ -165,35 +165,12 @@ TEST_VM_F(ShenandoahOldGenerationTest, test_actual_size_exceeds_promotion_reserv
       EXPECT_FALSE(promotions_enabled()) << "New plab can only be used for evacuations";
     }
     
    -TEST_VM_F(ShenandoahOldGenerationTest, test_shared_expends_promoted_but_does_not_change_plab) {
    +TEST_VM_F(ShenandoahOldGenerationTest, test_expend_promoted_should_increase_expended) {
       SKIP_IF_NOT_SHENANDOAH();
    -  ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(128, ShenandoahAffiliation::OLD_GENERATION, true);
    -  req.set_actual_size(128);
    -  size_t actual_size = req.actual_size() * HeapWordSize;
    -
       size_t expended_before = old->get_promoted_expended();
    -  old->configure_plab_for_current_thread(req);
    +  old->expend_promoted(128);
       size_t expended_after = old->get_promoted_expended();
    -
    -  EXPECT_EQ(expended_before + actual_size, expended_after) << "Shared promotion still expends promotion";
    -  EXPECT_EQ(plab_promoted(), INITIAL_PLAB_PROMOTED) << "Shared promotion should not count in plab";
    -  EXPECT_EQ(plab_size(), INITIAL_PLAB_SIZE) << "Shared promotion should not change size of plab";
    -  EXPECT_FALSE(promotions_enabled());
    -}
    -
    -TEST_VM_F(ShenandoahOldGenerationTest, test_shared_evacuation_has_no_side_effects) {
    -  SKIP_IF_NOT_SHENANDOAH();
    -  ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(128, ShenandoahAffiliation::OLD_GENERATION, false);
    -  req.set_actual_size(128);
    -
    -  size_t expended_before = old->get_promoted_expended();
    -  old->configure_plab_for_current_thread(req);
    -  size_t expended_after = old->get_promoted_expended();
    -
    -  EXPECT_EQ(expended_before, expended_after) << "Not a promotion, should not expend promotion reserve";
    -  EXPECT_EQ(plab_promoted(), INITIAL_PLAB_PROMOTED) << "Not a plab, should not have touched plab";
    -  EXPECT_EQ(plab_size(), INITIAL_PLAB_SIZE) << "Not a plab, should not have touched plab";
    -  EXPECT_FALSE(promotions_enabled());
    +  EXPECT_EQ(expended_before + 128, expended_after) << "Should expend promotion";
     }
     
     #undef SKIP_IF_NOT_SHENANDOAH
    diff --git a/test/hotspot/gtest/gtestMain.cpp b/test/hotspot/gtest/gtestMain.cpp
    index 842b6547b48..a1869ab5499 100644
    --- a/test/hotspot/gtest/gtestMain.cpp
    +++ b/test/hotspot/gtest/gtestMain.cpp
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -22,6 +22,7 @@
      *
      */
     
    +#include "cppstdlib/cstdlib.hpp"
     #include "jni.h"
     #include "runtime/os.hpp"
     #include "runtime/thread.inline.hpp"
    @@ -31,7 +32,6 @@
     
     #include 
     #include 
    -#include 
     #ifdef __APPLE__
     #include 
     #endif
    diff --git a/test/hotspot/gtest/jfr/test_adaptiveSampler.cpp b/test/hotspot/gtest/jfr/test_adaptiveSampler.cpp
    index 69548b06e51..8625f64099d 100644
    --- a/test/hotspot/gtest/jfr/test_adaptiveSampler.cpp
    +++ b/test/hotspot/gtest/jfr/test_adaptiveSampler.cpp
    @@ -34,13 +34,12 @@
     
     #include "jfr/utilities/jfrAllocation.hpp"
     #include "jfr/utilities/jfrRandom.inline.hpp"
    -#include "jfr/utilities/jfrSpinlockHelper.hpp"
     #include "jfr/utilities/jfrTime.hpp"
     #include "jfr/utilities/jfrTimeConverter.hpp"
     #include "jfr/utilities/jfrTryLock.hpp"
     #include "logging/log.hpp"
    -#include "runtime/atomicAccess.hpp"
     #include "utilities/globalDefinitions.hpp"
    +#include "utilities/spinCriticalSection.hpp"
     #include "unittest.hpp"
     
     #include 
    diff --git a/test/hotspot/gtest/nmt/test_regions_tree.cpp b/test/hotspot/gtest/nmt/test_regions_tree.cpp
    index a7f4a6962ca..7465c84aa72 100644
    --- a/test/hotspot/gtest/nmt/test_regions_tree.cpp
    +++ b/test/hotspot/gtest/nmt/test_regions_tree.cpp
    @@ -48,7 +48,7 @@ TEST_VM_F(NMTRegionsTreeTest, ReserveCommitTwice) {
       }
       {
         VMATree::SummaryDiff diff, not_used;
    -    rt.commit_region(0, 50, ncs, not_used);
    +    rt.commit_region(nullptr, 50, ncs, not_used);
         rt.reserve_mapping(0, 100, rd, diff);
         EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve);
         EXPECT_EQ(-50, diff.tag[NMTUtil::tag_to_index(mtTest)].commit);
    @@ -62,10 +62,10 @@ TEST_VM_F(NMTRegionsTreeTest, ReserveCommitTwice) {
     
       {
         VMATree::SummaryDiff diff1, diff2;
    -    rt.commit_region(0, 50, ncs, diff1);
    +    rt.commit_region(nullptr, 50, ncs, diff1);
         EXPECT_EQ(0, diff1.tag[NMTUtil::tag_to_index(mtGC)].reserve);
         EXPECT_EQ(50, diff1.tag[NMTUtil::tag_to_index(mtGC)].commit);
    -    rt.commit_region(0, 50, ncs, diff2);
    +    rt.commit_region(nullptr, 50, ncs, diff2);
         EXPECT_EQ(0, diff2.tag[NMTUtil::tag_to_index(mtTest)].reserve);
         EXPECT_EQ(0, diff2.tag[NMTUtil::tag_to_index(mtTest)].commit);
       }
    @@ -78,7 +78,7 @@ TEST_VM_F(NMTRegionsTreeTest, CommitUncommitRegion) {
       rt.reserve_mapping(0, 100, rd, not_used);
       {
         VMATree::SummaryDiff diff;
    -    rt.commit_region(0, 50, ncs, diff);
    +    rt.commit_region(nullptr, 50, ncs, diff);
         EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve);
         EXPECT_EQ(50, diff.tag[NMTUtil::tag_to_index(mtTest)].commit);
       }
    @@ -90,7 +90,7 @@ TEST_VM_F(NMTRegionsTreeTest, CommitUncommitRegion) {
       }
       {
         VMATree::SummaryDiff diff;
    -    rt.uncommit_region(0, 50, diff);
    +    rt.uncommit_region(nullptr, 50, diff);
         EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve);
         EXPECT_EQ(-50, diff.tag[NMTUtil::tag_to_index(mtTest)].commit);
       }
    @@ -153,4 +153,4 @@ TEST_VM_F(NMTRegionsTreeTest, VisitCommittedRegions) {
         return true;
       });
       EXPECT_EQ(count, 4UL);
    -}
    \ No newline at end of file
    +}
    diff --git a/test/hotspot/gtest/oops/test_compressedKlass.cpp b/test/hotspot/gtest/oops/test_compressedKlass.cpp
    index c2ae7719776..dcdb335adb5 100644
    --- a/test/hotspot/gtest/oops/test_compressedKlass.cpp
    +++ b/test/hotspot/gtest/oops/test_compressedKlass.cpp
    @@ -1,6 +1,6 @@
     /*
      * Copyright (c) 2024, 2025, Red Hat, Inc. All rights reserved.
    - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -32,7 +32,6 @@ TEST_VM(CompressedKlass, basics) {
       if (!UseCompressedClassPointers) {
         return;
       }
    -  ASSERT_LE((address)0, CompressedKlassPointers::base());
       ASSERT_LE(CompressedKlassPointers::base(), CompressedKlassPointers::klass_range_start());
       ASSERT_LT(CompressedKlassPointers::klass_range_start(), CompressedKlassPointers::klass_range_end());
       ASSERT_LE(CompressedKlassPointers::klass_range_end(), CompressedKlassPointers::encoding_range_end());
    @@ -50,7 +49,7 @@ TEST_VM(CompressedKlass, basics) {
         ASSERT_EQ(CompressedKlassPointers::encoding_range_end() - CompressedKlassPointers::base(), (ptrdiff_t)expected_size);
       }
     #else
    -  ASSERT_EQ(CompressedKlassPointers::base(), (address)0);
    +  ASSERT_EQ(CompressedKlassPointers::base(), nullptr);
       ASSERT_EQ(CompressedKlassPointers::encoding_range_end(), (address)(UINT_MAX));
     #endif // _LP64
     }
    diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp
    index 1a62941a486..fd49050d022 100644
    --- a/test/hotspot/gtest/opto/test_rangeinference.cpp
    +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -25,15 +25,16 @@
     #include "opto/rangeinference.hpp"
     #include "opto/type.hpp"
     #include "runtime/os.hpp"
    -#include "utilities/intn_t.hpp"
     #include "unittest.hpp"
    +#include "utilities/intn_t.hpp"
    +#include "utilities/rbTree.hpp"
    +#include 
    +#include 
    +#include 
     
     template 
    -static U uniform_random();
    -
    -template <>
    -juint uniform_random() {
    -  return os::random();
    +static U uniform_random() {
    +  return U(juint(os::random()));
     }
     
     template <>
    @@ -212,3 +213,413 @@ TEST_VM(opto, canonicalize_constraints) {
       test_canonicalize_constraints_random();
       test_canonicalize_constraints_random();
     }
    +
    +// Implementations of TypeIntMirror methods for testing purposes
    +template 
    +TypeIntMirror TypeIntMirror::make(const TypeIntMirror& t, int widen) {
    +  return t;
    +}
    +
    +template 
    +const TypeIntMirror* TypeIntMirror::operator->() const {
    +  return this;
    +}
    +
    +template 
    +bool TypeIntMirror::contains(U u) const {
    +  S s = S(u);
    +  return s >= _lo && s <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u);
    +}
    +
    +template 
    +bool TypeIntMirror::contains(const TypeIntMirror& o) const {
    +  return TypeIntHelper::int_type_is_subset(*this, o);
    +}
    +
    +template 
    +bool TypeIntMirror::operator==(const TypeIntMirror& o) const {
    +  return TypeIntHelper::int_type_is_equal(*this, o);
    +}
    +
    +template 
    +template 
    +TypeIntMirror TypeIntMirror::cast() const {
    +  static_assert(std::is_same_v);
    +  return *this;
    +}
    +
    +// The number of TypeIntMirror instances for integral types with a few bits. These values are
    +// calculated once and written down for usage in constexpr contexts.
    +template 
    +static constexpr size_t all_instances_size() {
    +  using U = decltype(CTP::_ulo);
    +  constexpr juint max_unsigned = juint(std::numeric_limits::max());
    +  if constexpr (max_unsigned == 1U) {
    +    // 1 bit
    +    return 3;
    +  } else if constexpr (max_unsigned == 3U) {
    +    // 2 bits
    +    return 15;
    +  } else if constexpr (max_unsigned == 7U) {
    +    // 3 bits
    +    return 134;
    +  } else {
    +    // 4 bits
    +    static_assert(max_unsigned == 15U);
    +    // For more than 4 bits, the number of instances is too large and it is not realistic to
    +    // compute all of them.
    +    return 1732;
    +  }
    +}
    +
    +template 
    +static std::array()> compute_all_instances() {
    +  using S = decltype(CTP::_lo);
    +  using U = decltype(CTP::_ulo);
    +
    +  class CTPComparator {
    +  public:
    +    static RBTreeOrdering cmp(const CTP& x, const RBNode* node) {
    +      // Quick helper for the tediousness below
    +      auto f = [](auto x, auto y) {
    +        assert(x != y, "we only handle lt and gt cases");
    +        return x < y ? RBTreeOrdering::LT : RBTreeOrdering::GT;
    +      };
    +
    +      const CTP& y = node->key();
    +      if (x._lo != y._lo) {
    +        return f(x._lo, y._lo);
    +      } else if (x._hi != y._hi) {
    +        return f(x._hi, y._hi);
    +      } else if (x._ulo != y._ulo) {
    +        return f(x._ulo, y._ulo);
    +      } else if (x._uhi != y._uhi) {
    +        return f(x._uhi, y._uhi);
    +      } else if (x._bits._zeros != y._bits._zeros) {
    +        return f(x._bits._zeros, y._bits._zeros);
    +      } else if (x._bits._ones != y._bits._ones) {
    +        return f(x._bits._ones, y._bits._ones);
    +      } else {
    +        return RBTreeOrdering::EQ;
    +      }
    +    }
    +  };
    +
    +  RBTreeCHeap collector;
    +  for (jint lo = jint(std::numeric_limits::min()); lo <= jint(std::numeric_limits::max()); lo++) {
    +    for (jint hi = lo; hi <= jint(std::numeric_limits::max()); hi++) {
    +      for (juint ulo = 0; ulo <= juint(std::numeric_limits::max()); ulo++) {
    +        for (juint uhi = ulo; uhi <= juint(std::numeric_limits::max()); uhi++) {
    +          for (juint zeros = 0; zeros <= juint(std::numeric_limits::max()); zeros++) {
    +            for (juint ones = 0; ones <= juint(std::numeric_limits::max()); ones++) {
    +              TypeIntPrototype t{{S(lo), S(hi)}, {U(ulo), U(uhi)}, {U(zeros), U(ones)}};
    +              auto canonicalized_t = t.canonicalize_constraints();
    +              if (canonicalized_t.empty()) {
    +                continue;
    +              }
    +
    +              TypeIntPrototype ct = canonicalized_t._data;
    +              collector.upsert(CTP{ct._srange._lo, ct._srange._hi, ct._urange._lo, ct._urange._hi, ct._bits}, 0);
    +            }
    +          }
    +        }
    +      }
    +    }
    +  }
    +
    +  assert(collector.size() == all_instances_size(), "unexpected size of all_instance, expected %d, actual %d", jint(all_instances_size()), jint(collector.size()));
    +  std::array()> res;
    +  size_t idx = 0;
    +  collector.visit_in_order([&](RBNode* node) {
    +    res[idx] = node->key();
    +    idx++;
    +    return true;
    +  });
    +  return res;
    +}
    +
    +template 
    +static const std::array()>& all_instances() {
    +  static std::array()> res = compute_all_instances();
    +  static_assert(std::is_trivially_destructible_v);
    +  return res;
    +}
    +
    +// Check the correctness, that is, if v1 is an element of input1, v2 is an element of input2, then
    +// op(v1, v2) must be an element of infer(input1, input2). This version does the check exhaustively
    +// on all elements of input1 and input2.
    +template 
    +static void test_binary_instance_correctness_exhaustive(Operation op, Inference infer, const InputType& input1, const InputType& input2) {
    +  using S = std::remove_const_t_lo)>;
    +  using U = std::remove_const_t_ulo)>;
    +  InputType result = infer(input1, input2);
    +
    +  for (juint v1 = juint(std::numeric_limits::min()); v1 <= juint(std::numeric_limits::max()); v1++) {
    +    if (!input1.contains(U(v1))) {
    +      continue;
    +    }
    +
    +    for (juint v2 = juint(std::numeric_limits::min()); v2 <= juint(std::numeric_limits::max()); v2++) {
    +      if (!input2.contains(U(v2))) {
    +        continue;
    +      }
    +
    +      U r = op(U(v1), U(v2));
    +      ASSERT_TRUE(result.contains(r));
    +    }
    +  }
    +}
    +
    +// Check the correctness, that is, if v1 is an element of input1, v2 is an element of input2, then
    +// op(v1, v2) must be an element of infer(input1, input2). This version does the check randomly on
    +// a number of elements in input1 and input2.
    +template 
    +static void test_binary_instance_correctness_samples(Operation op, Inference infer, const InputType& input1, const InputType& input2) {
    +  using U = std::remove_const_t_ulo)>;
    +  auto result = infer(input1, input2);
    +
    +  constexpr size_t sample_count = 6;
    +  U input1_samples[sample_count] {U(input1._lo), U(input1._hi), input1._ulo, input1._uhi, input1._ulo, input1._ulo};
    +  U input2_samples[sample_count] {U(input2._lo), U(input2._hi), input2._ulo, input2._uhi, input2._ulo, input2._ulo};
    +
    +  auto random_sample = [](U* samples, const InputType& input) {
    +    constexpr size_t max_tries = 100;
    +    constexpr size_t start_random_idx = 4;
    +    for (size_t tries = 0, idx = start_random_idx; tries < max_tries && idx < sample_count; tries++) {
    +      U n = uniform_random();
    +      if (input.contains(n)) {
    +        samples[idx] = n;
    +        idx++;
    +      }
    +    }
    +  };
    +  random_sample(input1_samples, input1);
    +  random_sample(input2_samples, input2);
    +
    +  for (size_t i = 0; i < sample_count; i++) {
    +    for (size_t j = 0; j < sample_count; j++) {
    +      U r = op(input1_samples[i], input2_samples[j]);
    +      ASSERT_TRUE(result.contains(r));
    +    }
    +  }
    +}
    +
    +// Check the monotonicity, that is, if input1 is a subset of super1, input2 is a subset of super2,
    +// then infer(input1, input2) must be a subset of infer(super1, super2). This version does the
    +// check exhaustively on all supersets of input1 and input2.
    +template 
    +static void test_binary_instance_monotonicity_exhaustive(Inference infer, const InputType& input1, const InputType& input2) {
    +  InputType result = infer(input1, input2);
    +
    +  for (const InputType& super1 : all_instances()) {
    +    if (!super1.contains(input1) || super1 == input1) {
    +      continue;
    +    }
    +
    +    for (const InputType& super2 : all_instances()) {
    +      if (!super2.contains(input2) || super2 == input2) {
    +        continue;
    +      }
    +
    +      ASSERT_TRUE(infer(input1, super2).contains(result));
    +      ASSERT_TRUE(infer(super1, input2).contains(result));
    +      ASSERT_TRUE(infer(super1, super2).contains(result));
    +    }
    +  }
    +}
    +
    +// Check the monotonicity, that is, if input1 is a subset of super1, input2 is a subset of super2,
    +// then infer(input1, input2) must be a subset of infer(super1, super2). This version does the
    +// check randomly on a number of supersets of input1 and input2.
    +template 
    +static void test_binary_instance_monotonicity_samples(Inference infer, const InputType& input1, const InputType& input2) {
    +  using S = std::remove_const_t_lo)>;
    +  using U = std::remove_const_t_ulo)>;
    +  auto result = infer(input1, input2);
    +
    +  // The set that is a superset of all other sets
    +  InputType universe = InputType{std::numeric_limits::min(), std::numeric_limits::max(), U(0), U(-1), {U(0), U(0)}};
    +  ASSERT_TRUE(infer(universe, input2).contains(result));
    +  ASSERT_TRUE(infer(input1, universe).contains(result));
    +  ASSERT_TRUE(infer(universe, universe).contains(result));
    +
    +  auto random_superset = [](const InputType& input) {
    +    S lo = MIN2(input->_lo, S(uniform_random()));
    +    S hi = MAX2(input->_hi, S(uniform_random()));
    +    U ulo = MIN2(input->_ulo, uniform_random());
    +    U uhi = MAX2(input->_uhi, uniform_random());
    +    U zeros = input->_bits._zeros & uniform_random();
    +    U ones = input->_bits._ones & uniform_random();
    +    InputType super = InputType::make(TypeIntPrototype{{lo, hi}, {ulo, uhi}, {zeros, ones}}, 0);
    +    assert(super.contains(input), "impossible");
    +    return super;
    +  };
    +
    +  InputType super1 = random_superset(input1);
    +  InputType super2 = random_superset(input2);
    +  ASSERT_TRUE(infer(super1, input2).contains(result));
    +  ASSERT_TRUE(infer(input1, super2).contains(result));
    +  ASSERT_TRUE(infer(super1, super2).contains(result));
    +}
    +
    +// Verify the correctness and monotonicity of an inference function by exhautively analyzing all
    +// instances of InputType
    +template 
    +static void test_binary_exhaustive(Operation op, Inference infer) {
    +  for (const InputType& input1 : all_instances()) {
    +    for (const InputType& input2 : all_instances()) {
    +      test_binary_instance_correctness_exhaustive(op, infer, input1, input2);
    +      if (all_instances().size() < 100) {
    +        // This effectively covers the cases up to uintn_t<2>
    +        test_binary_instance_monotonicity_exhaustive(infer, input1, input2);
    +      } else {
    +        // This effectively covers the cases of uintn_t<3>
    +        test_binary_instance_monotonicity_samples(infer, input1, input2);
    +      }
    +    }
    +  }
    +}
    +
    +// Verify the correctness and monotonicity of an inference function by randomly sampling instances
    +// of InputType
    +template 
    +static void test_binary_random(Operation op, Inference infer) {
    +  using S = std::remove_const_t;
    +  using U = std::remove_const_t;
    +
    +  constexpr size_t sample_count = 100;
    +  InputType samples[sample_count];
    +
    +  // Fill with {0}
    +  for (size_t i = 0; i < sample_count; i++) {
    +    samples[i] = InputType::make(TypeIntPrototype{{S(0), S(0)}, {U(0), U(0)}, {U(0), U(0)}}, 0);
    +  }
    +
    +  // {1}
    +  samples[1] = InputType::make(TypeIntPrototype{{S(1), S(1)}, {U(1), U(1)}, {U(0), U(0)}}, 0);
    +  // {-1}
    +  samples[2] = InputType::make(TypeIntPrototype{{S(-1), S(-1)}, {U(-1), U(-1)}, {U(0), U(0)}}, 0);
    +  // {0, 1}
    +  samples[3] = InputType::make(TypeIntPrototype{{S(0), S(1)}, {U(0), U(1)}, {U(0), U(0)}}, 0);
    +  // {-1, 0, 1}
    +  samples[4] = InputType::make(TypeIntPrototype{{S(-1), S(1)}, {U(0), U(-1)}, {U(0), U(0)}}, 0);
    +  // {-1, 1}
    +  samples[5] = InputType::make(TypeIntPrototype{{S(-1), S(1)}, {U(1), U(-1)}, {U(0), U(0)}}, 0);
    +  // {0, 1, 2}
    +  samples[6] = InputType::make(TypeIntPrototype{{S(0), S(2)}, {U(0), U(2)}, {U(0), U(0)}}, 0);
    +  // {0, 2}
    +  samples[7] = InputType::make(TypeIntPrototype{{S(0), S(2)}, {U(0), U(2)}, {U(1), U(0)}}, 0);
    +  // [min_signed, max_signed]
    +  samples[8] = InputType::make(TypeIntPrototype{{std::numeric_limits::min(), std::numeric_limits::max()}, {U(0), U(-1)}, {U(0), U(0)}}, 0);
    +  // [0, max_signed]
    +  samples[9] = InputType::make(TypeIntPrototype{{S(0), std::numeric_limits::max()}, {U(0), U(-1)}, {U(0), U(0)}}, 0);
    +  // [min_signed, 0)
    +  samples[10] = InputType::make(TypeIntPrototype{{std::numeric_limits::min(), S(-1)}, {U(0), U(-1)}, {U(0), U(0)}}, 0);
    +
    +  constexpr size_t max_tries = 1000;
    +  constexpr size_t start_random_idx = 11;
    +  for (size_t tries = 0, idx = start_random_idx; tries < max_tries && idx < sample_count; tries++) {
    +    // Try to have lo < hi
    +    S signed_bound1 = S(uniform_random());
    +    S signed_bound2 = S(uniform_random());
    +    S lo = MIN2(signed_bound1, signed_bound2);
    +    S hi = MAX2(signed_bound1, signed_bound2);
    +
    +    // Try to have ulo < uhi
    +    U unsigned_bound1 = uniform_random();
    +    U unsigned_bound2 = uniform_random();
    +    U ulo = MIN2(unsigned_bound1, unsigned_bound2);
    +    U uhi = MAX2(unsigned_bound1, unsigned_bound2);
    +
    +    // Try to have (zeros & ones) == 0
    +    U zeros = uniform_random();
    +    U ones = uniform_random();
    +    U common = zeros & ones;
    +    zeros = zeros ^ common;
    +    ones = ones ^ common;
    +
    +    TypeIntPrototype t{{lo, hi}, {ulo, uhi}, {zeros, ones}};
    +    auto canonicalized_t = t.canonicalize_constraints();
    +    if (canonicalized_t.empty()) {
    +      continue;
    +    }
    +
    +    samples[idx] = TypeIntMirror{canonicalized_t._data._srange._lo, canonicalized_t._data._srange._hi,
    +                                       canonicalized_t._data._urange._lo, canonicalized_t._data._urange._hi,
    +                                       canonicalized_t._data._bits};
    +    idx++;
    +  }
    +
    +  for (size_t i = 0; i < sample_count; i++) {
    +    for (size_t j = 0; j < sample_count; j++) {
    +      test_binary_instance_correctness_samples(op, infer, samples[i], samples[j]);
    +      test_binary_instance_monotonicity_samples(infer, samples[i], samples[j]);
    +    }
    +  }
    +}
    +
    +template