Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 23f4810c46 |
@@ -149,7 +149,7 @@ define SetupExecuteBody
|
||||
endif
|
||||
|
||||
$1_VARDEPS := $$($1_COMMAND) $$($1_PRE_COMMAND) $$($1_POST_COMMAND)
|
||||
$1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, $$($1_BASE)_exec.vardeps)
|
||||
$1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS)
|
||||
|
||||
ifneq ($$($1_PRE_COMMAND), )
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ void VM_Version::common_initialize() {
|
||||
|
||||
if (FLAG_IS_DEFAULT(AvoidUnalignedAccesses)) {
|
||||
FLAG_SET_DEFAULT(AvoidUnalignedAccesses,
|
||||
unaligned_scalar.value() != MISALIGNED_SCALAR_FAST);
|
||||
unaligned_access.value() != MISALIGNED_FAST);
|
||||
}
|
||||
|
||||
if (!AvoidUnalignedAccesses) {
|
||||
@@ -175,12 +175,7 @@ void VM_Version::common_initialize() {
|
||||
// This machine has fast unaligned memory accesses
|
||||
if (FLAG_IS_DEFAULT(UseUnalignedAccesses)) {
|
||||
FLAG_SET_DEFAULT(UseUnalignedAccesses,
|
||||
(unaligned_scalar.value() == MISALIGNED_SCALAR_FAST));
|
||||
}
|
||||
|
||||
if (FLAG_IS_DEFAULT(AlignVector) && unaligned_vector.enabled()) {
|
||||
FLAG_SET_DEFAULT(AlignVector,
|
||||
unaligned_vector.value() != MISALIGNED_VECTOR_FAST);
|
||||
unaligned_access.value() == MISALIGNED_FAST);
|
||||
}
|
||||
|
||||
#ifdef __riscv_ztso
|
||||
|
||||
@@ -191,8 +191,7 @@ class VM_Version : public Abstract_VM_Version {
|
||||
// mvendorid Manufactory JEDEC id encoded, ISA vol 2 3.1.2..
|
||||
// marchid Id for microarch. Mvendorid plus marchid uniquely identify the microarch.
|
||||
// mimpid A unique encoding of the version of the processor implementation.
|
||||
// unaligned_scalar Performance of misaligned scalar accesses (unknown, emulated, slow, fast, unsupported)
|
||||
// unaligned_vector Performance of misaligned vector accesses (unknown, unspported, slow, fast)
|
||||
// unaligned_access Unaligned memory accesses (unknown, unspported, emulated, slow, firmware, fast)
|
||||
// satp mode SATP bits (number of virtual addr bits) mbare, sv39, sv48, sv57, sv64
|
||||
|
||||
public:
|
||||
@@ -252,12 +251,11 @@ class VM_Version : public Abstract_VM_Version {
|
||||
// Non-extension features
|
||||
//
|
||||
#define RV_NON_EXT_FEATURE_FLAGS(decl) \
|
||||
decl(unaligned_access , Unaligned , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
|
||||
decl(mvendorid , VendorId , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
|
||||
decl(marchid , ArchId , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
|
||||
decl(mimpid , ImpId , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
|
||||
decl(satp_mode , SATP , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
|
||||
decl(unaligned_scalar , UnalignedScalar , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
|
||||
decl(unaligned_vector , UnalignedVector , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
|
||||
decl(zicboz_block_size, ZicbozBlockSize , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
|
||||
|
||||
#define DECLARE_RV_NON_EXT_FEATURE(NAME, PRETTY, LINUX_BIT, FSTRING, FLAGF) \
|
||||
@@ -398,19 +396,12 @@ private:
|
||||
static VM_MODE parse_satp_mode(const char* vm_mode);
|
||||
|
||||
// Values from riscv_hwprobe()
|
||||
enum UNALIGNED_SCALAR_ACCESS : int {
|
||||
MISALIGNED_SCALAR_UNKNOWN = 0,
|
||||
MISALIGNED_SCALAR_EMULATED = 1,
|
||||
MISALIGNED_SCALAR_SLOW = 2,
|
||||
MISALIGNED_SCALAR_FAST = 3,
|
||||
MISALIGNED_SCALAR_UNSUPPORTED = 4
|
||||
};
|
||||
|
||||
enum UNALIGNED_VECTOR_ACCESS : int {
|
||||
MISALIGNED_VECTOR_UNKNOWN = 0,
|
||||
MISALIGNED_VECTOR_SLOW = 2,
|
||||
MISALIGNED_VECTOR_FAST = 3,
|
||||
MISALIGNED_VECTOR_UNSUPPORTED = 4
|
||||
enum UNALIGNED_ACCESS : int {
|
||||
MISALIGNED_UNKNOWN = 0,
|
||||
MISALIGNED_EMULATED = 1,
|
||||
MISALIGNED_SLOW = 2,
|
||||
MISALIGNED_FAST = 3,
|
||||
MISALIGNED_UNSUPPORTED = 4
|
||||
};
|
||||
|
||||
// Null terminated list
|
||||
|
||||
@@ -160,6 +160,7 @@ address os::Linux::_initial_thread_stack_bottom = nullptr;
|
||||
uintptr_t os::Linux::_initial_thread_stack_size = 0;
|
||||
|
||||
int (*os::Linux::_pthread_getcpuclockid)(pthread_t, clockid_t *) = nullptr;
|
||||
int (*os::Linux::_pthread_setname_np)(pthread_t, const char*) = nullptr;
|
||||
pthread_t os::Linux::_main_thread;
|
||||
bool os::Linux::_supports_fast_thread_cpu_time = false;
|
||||
const char * os::Linux::_libc_version = nullptr;
|
||||
@@ -4370,6 +4371,10 @@ void os::init(void) {
|
||||
// _main_thread points to the thread that created/loaded the JVM.
|
||||
Linux::_main_thread = pthread_self();
|
||||
|
||||
// retrieve entry point for pthread_setname_np
|
||||
Linux::_pthread_setname_np =
|
||||
(int(*)(pthread_t, const char*))dlsym(RTLD_DEFAULT, "pthread_setname_np");
|
||||
|
||||
check_pax();
|
||||
|
||||
// Check the availability of MADV_POPULATE_WRITE.
|
||||
@@ -4846,24 +4851,14 @@ uint os::processor_id() {
|
||||
}
|
||||
|
||||
void os::set_native_thread_name(const char *name) {
|
||||
char buf[16]; // according to glibc manpage, 16 chars incl. '/0'
|
||||
// We may need to truncate the thread name. Since a common pattern
|
||||
// for thread names is to be both longer than 15 chars and have a
|
||||
// trailing number ("DispatcherWorkerThread21", "C2 CompilerThread#54" etc),
|
||||
// we preserve the end of the thread name by truncating the middle
|
||||
// (e.g. "Dispatc..read21").
|
||||
const size_t len = strlen(name);
|
||||
if (len < sizeof(buf)) {
|
||||
strcpy(buf, name);
|
||||
} else {
|
||||
(void) os::snprintf(buf, sizeof(buf), "%.7s..%.6s", name, name + len - 6);
|
||||
if (Linux::_pthread_setname_np) {
|
||||
char buf [16]; // according to glibc manpage, 16 chars incl. '/0'
|
||||
(void) os::snprintf(buf, sizeof(buf), "%s", name);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
const int rc = Linux::_pthread_setname_np(pthread_self(), buf);
|
||||
// ERANGE should not happen; all other errors should just be ignored.
|
||||
assert(rc != ERANGE, "pthread_setname_np failed");
|
||||
}
|
||||
// Note: we use the system call here instead of calling pthread_setname_np
|
||||
// since this is the only way to make ASAN aware of our thread names. Even
|
||||
// though ASAN intercepts both prctl and pthread_setname_np, it only processes
|
||||
// the thread name given to the former.
|
||||
int rc = prctl(PR_SET_NAME, buf);
|
||||
assert(rc == 0, "prctl(PR_SET_NAME) failed");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -33,6 +33,7 @@ class os::Linux {
|
||||
friend class os;
|
||||
|
||||
static int (*_pthread_getcpuclockid)(pthread_t, clockid_t *);
|
||||
static int (*_pthread_setname_np)(pthread_t, const char*);
|
||||
|
||||
static address _initial_thread_stack_bottom;
|
||||
static uintptr_t _initial_thread_stack_size;
|
||||
|
||||
@@ -89,24 +89,7 @@
|
||||
#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0)
|
||||
#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0)
|
||||
|
||||
#define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6
|
||||
|
||||
#define RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS 7
|
||||
|
||||
#define RISCV_HWPROBE_KEY_TIME_CSR_FREQ 8
|
||||
|
||||
#define RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF 9
|
||||
#define RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN 0
|
||||
#define RISCV_HWPROBE_MISALIGNED_SCALAR_EMULATED 1
|
||||
#define RISCV_HWPROBE_MISALIGNED_SCALAR_SLOW 2
|
||||
#define RISCV_HWPROBE_MISALIGNED_SCALAR_FAST 3
|
||||
#define RISCV_HWPROBE_MISALIGNED_SCALAR_UNSUPPORTED 4
|
||||
|
||||
#define RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF 10
|
||||
#define RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN 0
|
||||
#define RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW 2
|
||||
#define RISCV_HWPROBE_MISALIGNED_VECTOR_FAST 3
|
||||
#define RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED 4
|
||||
#define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6
|
||||
|
||||
#ifndef NR_riscv_hwprobe
|
||||
#ifndef NR_arch_specific_syscall
|
||||
@@ -134,11 +117,7 @@ static struct riscv_hwprobe query[] = {{RISCV_HWPROBE_KEY_MVENDORID, 0},
|
||||
{RISCV_HWPROBE_KEY_BASE_BEHAVIOR, 0},
|
||||
{RISCV_HWPROBE_KEY_IMA_EXT_0, 0},
|
||||
{RISCV_HWPROBE_KEY_CPUPERF_0, 0},
|
||||
{RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE, 0},
|
||||
{RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS, 0},
|
||||
{RISCV_HWPROBE_KEY_TIME_CSR_FREQ, 0},
|
||||
{RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF, 0},
|
||||
{RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF, 0}};
|
||||
{RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE, 0}};
|
||||
|
||||
bool RiscvHwprobe::probe_features() {
|
||||
assert(!rw_hwprobe_completed, "Called twice.");
|
||||
@@ -267,16 +246,10 @@ void RiscvHwprobe::add_features_from_query_result() {
|
||||
VM_Version::ext_Zicond.enable_feature();
|
||||
}
|
||||
#endif
|
||||
// RISCV_HWPROBE_KEY_CPUPERF_0 is deprecated. Keep it there for backward
|
||||
// compatibility with old kernels.
|
||||
if (is_valid(RISCV_HWPROBE_KEY_CPUPERF_0)) {
|
||||
VM_Version::unaligned_scalar.enable_feature(
|
||||
VM_Version::unaligned_access.enable_feature(
|
||||
query[RISCV_HWPROBE_KEY_CPUPERF_0].value & RISCV_HWPROBE_MISALIGNED_MASK);
|
||||
}
|
||||
if (is_valid(RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF)) {
|
||||
VM_Version::unaligned_vector.enable_feature(
|
||||
query[RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF].value);
|
||||
}
|
||||
if (is_valid(RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE)) {
|
||||
VM_Version::zicboz_block_size.enable_feature(query[RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE].value);
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ void VM_Version::rivos_features() {
|
||||
|
||||
ext_Zvfh.enable_feature();
|
||||
|
||||
unaligned_scalar.enable_feature(MISALIGNED_SCALAR_FAST);
|
||||
unaligned_access.enable_feature(MISALIGNED_FAST);
|
||||
satp_mode.enable_feature(VM_SV48);
|
||||
|
||||
// Features dependent on march/mimpid.
|
||||
|
||||
@@ -53,7 +53,7 @@ void DumpTimeSharedClassTable::iterate_all_live_classes(Function function) const
|
||||
assert(k->is_loader_alive(), "must not change");
|
||||
} else {
|
||||
if (!SystemDictionaryShared::is_excluded_class(k)) {
|
||||
SystemDictionaryShared::log_exclusion(k, "Class loader not alive");
|
||||
SystemDictionaryShared::warn_excluded(k, "Class loader not alive");
|
||||
SystemDictionaryShared::set_excluded_locked(k);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ void Modules::define_module(Handle module, jboolean is_open, jstring version,
|
||||
#if COMPILER2_OR_JVMCI
|
||||
// Special handling of jdk.incubator.vector
|
||||
if (strcmp(module_name, "jdk.incubator.vector") == 0) {
|
||||
if (FLAG_IS_DEFAULT(EnableVectorSupport) RISCV64_ONLY(&& !AlignVector)) {
|
||||
if (FLAG_IS_DEFAULT(EnableVectorSupport)) {
|
||||
FLAG_SET_DEFAULT(EnableVectorSupport, true);
|
||||
}
|
||||
if (EnableVectorSupport && FLAG_IS_DEFAULT(EnableVectorReboxing)) {
|
||||
|
||||
@@ -354,13 +354,11 @@ void SystemDictionaryShared::check_exclusion_for_self_and_dependencies(InstanceK
|
||||
});
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::log_exclusion(InstanceKlass* k, const char* reason, bool is_warning) {
|
||||
// Returns true so the caller can do: return warn_excluded(".....");
|
||||
bool SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) {
|
||||
ResourceMark rm;
|
||||
if (is_warning) {
|
||||
aot_log_warning(aot)("Skipping %s: %s", k->name()->as_C_string(), reason);
|
||||
} else {
|
||||
aot_log_info(aot)("Skipping %s: %s", k->name()->as_C_string(), reason);
|
||||
}
|
||||
aot_log_warning(aot)("Skipping %s: %s", k->name()->as_C_string(), reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) {
|
||||
@@ -379,35 +377,23 @@ bool SystemDictionaryShared::is_early_klass(InstanceKlass* ik) {
|
||||
}
|
||||
|
||||
bool SystemDictionaryShared::check_self_exclusion(InstanceKlass* k) {
|
||||
bool log_warning = false;
|
||||
const char* error = check_self_exclusion_helper(k, log_warning);
|
||||
if (error != nullptr) {
|
||||
log_exclusion(k, error, log_warning);
|
||||
return true; // Should be excluded
|
||||
} else {
|
||||
return false; // Should not be excluded
|
||||
}
|
||||
}
|
||||
|
||||
const char* SystemDictionaryShared::check_self_exclusion_helper(InstanceKlass* k, bool& log_warning) {
|
||||
assert_lock_strong(DumpTimeTable_lock);
|
||||
if (CDSConfig::is_dumping_final_static_archive() && k->defined_by_other_loaders()
|
||||
&& k->in_aot_cache()) {
|
||||
return nullptr; // Do not exclude: unregistered classes are passed from preimage to final image.
|
||||
return false; // Do not exclude: unregistered classes are passed from preimage to final image.
|
||||
}
|
||||
|
||||
if (k->is_in_error_state()) {
|
||||
log_warning = true;
|
||||
return "In error state";
|
||||
return warn_excluded(k, "In error state");
|
||||
}
|
||||
if (k->is_scratch_class()) {
|
||||
return "A scratch class";
|
||||
return warn_excluded(k, "A scratch class");
|
||||
}
|
||||
if (!k->is_loaded()) {
|
||||
return "Not in loaded state";
|
||||
return warn_excluded(k, "Not in loaded state");
|
||||
}
|
||||
if (has_been_redefined(k)) {
|
||||
return "Has been redefined";
|
||||
return warn_excluded(k, "Has been redefined");
|
||||
}
|
||||
if (!k->is_hidden() && k->shared_classpath_index() < 0 && is_builtin(k)) {
|
||||
if (k->name()->starts_with("java/lang/invoke/BoundMethodHandle$Species_")) {
|
||||
@@ -415,42 +401,43 @@ const char* SystemDictionaryShared::check_self_exclusion_helper(InstanceKlass* k
|
||||
if (CDSConfig::is_dumping_method_handles()) {
|
||||
k->set_shared_classpath_index(0);
|
||||
} else {
|
||||
return "dynamically generated";
|
||||
ResourceMark rm;
|
||||
aot_log_info(aot)("Skipping %s because it is dynamically generated", k->name()->as_C_string());
|
||||
return true; // exclude without warning
|
||||
}
|
||||
} else {
|
||||
// These are classes loaded from unsupported locations (such as those loaded by JVMTI native
|
||||
// agent during dump time).
|
||||
return "Unsupported location";
|
||||
return warn_excluded(k, "Unsupported location");
|
||||
}
|
||||
}
|
||||
if (k->signers() != nullptr) {
|
||||
// We cannot include signed classes in the archive because the certificates
|
||||
// used during dump time may be different than those used during
|
||||
// runtime (due to expiration, etc).
|
||||
return "Signed JAR";
|
||||
return warn_excluded(k, "Signed JAR");
|
||||
}
|
||||
if (is_jfr_event_class(k)) {
|
||||
// We cannot include JFR event classes because they need runtime-specific
|
||||
// instrumentation in order to work with -XX:FlightRecorderOptions:retransform=false.
|
||||
// There are only a small number of these classes, so it's not worthwhile to
|
||||
// support them and make CDS more complicated.
|
||||
return "JFR event class";
|
||||
return warn_excluded(k, "JFR event class");
|
||||
}
|
||||
|
||||
if (!k->is_linked()) {
|
||||
if (has_class_failed_verification(k)) {
|
||||
log_warning = true;
|
||||
return "Failed verification";
|
||||
return warn_excluded(k, "Failed verification");
|
||||
} else if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
// Most loaded classes should have been speculatively linked by AOTMetaspace::link_class_for_cds().
|
||||
// Old classes may not be linked if CDSConfig::is_preserving_verification_constraints()==false.
|
||||
// An unlinked class may fail to verify in AOTLinkedClassBulkLoader::init_required_classes_for_loader(),
|
||||
// causing the JVM to fail at bootstrap.
|
||||
return "Unlinked class not supported by AOTClassLinking";
|
||||
return warn_excluded(k, "Unlinked class not supported by AOTClassLinking");
|
||||
} else if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
// When dumping the final static archive, we will unconditionally load and link all
|
||||
// classes from the preimage. We don't want to get a VerifyError when linking this class.
|
||||
return "Unlinked class not supported by AOTConfiguration";
|
||||
return warn_excluded(k, "Unlinked class not supported by AOTConfiguration");
|
||||
}
|
||||
} else {
|
||||
if (!k->can_be_verified_at_dumptime()) {
|
||||
@@ -460,15 +447,17 @@ const char* SystemDictionaryShared::check_self_exclusion_helper(InstanceKlass* k
|
||||
// won't work at runtime.
|
||||
// As a result, we cannot store this class. It must be loaded and fully verified
|
||||
// at runtime.
|
||||
return "Old class has been linked";
|
||||
return warn_excluded(k, "Old class has been linked");
|
||||
}
|
||||
}
|
||||
|
||||
if (UnregisteredClasses::check_for_exclusion(k)) {
|
||||
return "used only when dumping CDS archive";
|
||||
ResourceMark rm;
|
||||
aot_log_info(aot)("Skipping %s: used only when dumping CDS archive", k->name()->as_C_string());
|
||||
return true;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if DumpTimeClassInfo::is_excluded() is true for at least one of k's exclusion dependencies.
|
||||
@@ -522,7 +511,7 @@ bool SystemDictionaryShared::is_dependency_excluded(InstanceKlass* k, InstanceKl
|
||||
DumpTimeClassInfo* dependency_info = get_info_locked(dependency);
|
||||
if (dependency_info->is_excluded()) {
|
||||
ResourceMark rm;
|
||||
aot_log_info(aot)("Skipping %s: %s %s is excluded", k->name()->as_C_string(), type, dependency->name()->as_C_string());
|
||||
aot_log_warning(aot)("Skipping %s: %s %s is excluded", k->name()->as_C_string(), type, dependency->name()->as_C_string());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -849,7 +838,7 @@ public:
|
||||
InstanceKlass* k = _list.at(i);
|
||||
bool i_am_first = SystemDictionaryShared::add_unregistered_class(_thread, k);
|
||||
if (!i_am_first) {
|
||||
SystemDictionaryShared::log_exclusion(k, "Duplicated unregistered class");
|
||||
SystemDictionaryShared::warn_excluded(k, "Duplicated unregistered class");
|
||||
SystemDictionaryShared::set_excluded_locked(k);
|
||||
}
|
||||
}
|
||||
@@ -978,7 +967,7 @@ bool SystemDictionaryShared::has_class_failed_verification(InstanceKlass* ik) {
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::set_from_class_file_load_hook(InstanceKlass* ik) {
|
||||
log_exclusion(ik, "From ClassFileLoadHook");
|
||||
warn_excluded(ik, "From ClassFileLoadHook");
|
||||
set_excluded(ik);
|
||||
}
|
||||
|
||||
|
||||
@@ -180,7 +180,6 @@ private:
|
||||
// exclusion checks
|
||||
static void check_exclusion_for_self_and_dependencies(InstanceKlass *k);
|
||||
static bool check_self_exclusion(InstanceKlass* k);
|
||||
static const char* check_self_exclusion_helper(InstanceKlass* k, bool& log_warning);
|
||||
static bool check_dependencies_exclusion(InstanceKlass* k, DumpTimeClassInfo* info);
|
||||
static bool check_verification_constraint_exclusion(InstanceKlass* k, Symbol* constraint_class_name);
|
||||
static bool is_dependency_excluded(InstanceKlass* k, InstanceKlass* dependency, const char* type);
|
||||
@@ -278,7 +277,7 @@ public:
|
||||
static void set_excluded(InstanceKlass* k);
|
||||
static void set_excluded_locked(InstanceKlass* k);
|
||||
static void set_from_class_file_load_hook(InstanceKlass* k) NOT_CDS_RETURN;
|
||||
static void log_exclusion(InstanceKlass* k, const char* reason, bool is_warning = false);
|
||||
static bool warn_excluded(InstanceKlass* k, const char* reason);
|
||||
static void dumptime_classes_do(class MetaspaceClosure* it);
|
||||
static void write_to_archive(bool is_static_archive = true);
|
||||
static void serialize_dictionary_headers(class SerializeClosure* soc,
|
||||
|
||||
@@ -237,7 +237,7 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) {
|
||||
// Exclude any classes that are verified with the old verifier, as the old verifier
|
||||
// doesn't call SystemDictionaryShared::add_verification_constraint()
|
||||
if (CDSConfig::is_dumping_archive()) {
|
||||
SystemDictionaryShared::log_exclusion(klass, "Verified with old verifier");
|
||||
SystemDictionaryShared::warn_excluded(klass, "Verified with old verifier");
|
||||
SystemDictionaryShared::set_excluded(klass);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1080,13 +1080,4 @@ public:
|
||||
static const Vptr _vpntr;
|
||||
};
|
||||
|
||||
struct NMethodMarkingScope : StackObj {
|
||||
NMethodMarkingScope() {
|
||||
nmethod::oops_do_marking_prologue();
|
||||
}
|
||||
~NMethodMarkingScope() {
|
||||
nmethod::oops_do_marking_epilogue();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_CODE_NMETHOD_HPP
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "gc/shared/referencePolicy.hpp"
|
||||
#include "gc/shared/referenceProcessorPhaseTimes.hpp"
|
||||
#include "gc/shared/space.hpp"
|
||||
#include "gc/shared/strongRootsScope.hpp"
|
||||
#include "gc/shared/weakProcessor.hpp"
|
||||
#include "memory/iterator.inline.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
@@ -482,6 +483,10 @@ void SerialFullGC::phase1_mark(bool clear_all_softrefs) {
|
||||
ref_processor()->start_discovery(clear_all_softrefs);
|
||||
|
||||
{
|
||||
StrongRootsScope srs(0);
|
||||
|
||||
MarkingNMethodClosure mark_code_closure(&follow_root_closure);
|
||||
|
||||
// Start tracing from roots, there are 3 kinds of roots in full-gc.
|
||||
//
|
||||
// 1. CLD. This method internally takes care of whether class loading is
|
||||
@@ -489,13 +494,8 @@ void SerialFullGC::phase1_mark(bool clear_all_softrefs) {
|
||||
// strong CLDs.
|
||||
ClassLoaderDataGraph::always_strong_cld_do(&follow_cld_closure);
|
||||
|
||||
{
|
||||
// 2. Threads stack frames and active nmethods in them.
|
||||
NMethodMarkingScope nmethod_marking_scope;
|
||||
MarkingNMethodClosure mark_code_closure(&follow_root_closure);
|
||||
|
||||
Threads::oops_do(&follow_root_closure, &mark_code_closure);
|
||||
}
|
||||
// 2. Threads stack frames and active nmethods in them.
|
||||
Threads::oops_do(&follow_root_closure, &mark_code_closure);
|
||||
|
||||
// 3. VM internal roots.
|
||||
OopStorageSet::strong_oops_do(&follow_root_closure);
|
||||
|
||||
@@ -43,7 +43,7 @@ ShenandoahAgeCensus::ShenandoahAgeCensus(uint max_workers)
|
||||
ShenandoahGenerationalMinTenuringAge, ShenandoahGenerationalMaxTenuringAge));
|
||||
}
|
||||
|
||||
_global_age_tables = NEW_C_HEAP_ARRAY(AgeTable*, MAX_SNAPSHOTS, mtGC);
|
||||
_global_age_table = NEW_C_HEAP_ARRAY(AgeTable*, MAX_SNAPSHOTS, mtGC);
|
||||
CENSUS_NOISE(_global_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, MAX_SNAPSHOTS, mtGC);)
|
||||
_tenuring_threshold = NEW_C_HEAP_ARRAY(uint, MAX_SNAPSHOTS, mtGC);
|
||||
CENSUS_NOISE(_skipped = 0);
|
||||
@@ -52,40 +52,36 @@ ShenandoahAgeCensus::ShenandoahAgeCensus(uint max_workers)
|
||||
|
||||
for (int i = 0; i < MAX_SNAPSHOTS; i++) {
|
||||
// Note that we don't now get perfdata from age_table
|
||||
_global_age_tables[i] = new AgeTable(false);
|
||||
_global_age_table[i] = new AgeTable(false);
|
||||
CENSUS_NOISE(_global_noise[i].clear();)
|
||||
// Sentinel value
|
||||
_tenuring_threshold[i] = MAX_COHORTS;
|
||||
}
|
||||
if (ShenandoahGenerationalAdaptiveTenuring) {
|
||||
_local_age_tables = NEW_C_HEAP_ARRAY(AgeTable*, _max_workers, mtGC);
|
||||
_local_age_table = NEW_C_HEAP_ARRAY(AgeTable*, _max_workers, mtGC);
|
||||
CENSUS_NOISE(_local_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, max_workers, mtGC);)
|
||||
for (uint i = 0; i < _max_workers; i++) {
|
||||
_local_age_tables[i] = new AgeTable(false);
|
||||
_local_age_table[i] = new AgeTable(false);
|
||||
CENSUS_NOISE(_local_noise[i].clear();)
|
||||
}
|
||||
} else {
|
||||
_local_age_tables = nullptr;
|
||||
}
|
||||
_epoch = MAX_SNAPSHOTS - 1; // see prepare_for_census_update()
|
||||
|
||||
if (!ShenandoahGenerationalAdaptiveTenuring) {
|
||||
_tenuring_threshold[_epoch] = InitialTenuringThreshold;
|
||||
_local_age_table = nullptr;
|
||||
}
|
||||
_epoch = MAX_SNAPSHOTS - 1; // see update_epoch()
|
||||
}
|
||||
|
||||
ShenandoahAgeCensus::~ShenandoahAgeCensus() {
|
||||
for (uint i = 0; i < MAX_SNAPSHOTS; i++) {
|
||||
delete _global_age_tables[i];
|
||||
delete _global_age_table[i];
|
||||
}
|
||||
FREE_C_HEAP_ARRAY(AgeTable*, _global_age_tables);
|
||||
FREE_C_HEAP_ARRAY(AgeTable*, _global_age_table);
|
||||
FREE_C_HEAP_ARRAY(uint, _tenuring_threshold);
|
||||
CENSUS_NOISE(FREE_C_HEAP_ARRAY(ShenandoahNoiseStats, _global_noise));
|
||||
if (_local_age_tables) {
|
||||
if (_local_age_table) {
|
||||
for (uint i = 0; i < _max_workers; i++) {
|
||||
delete _local_age_tables[i];
|
||||
delete _local_age_table[i];
|
||||
}
|
||||
FREE_C_HEAP_ARRAY(AgeTable*, _local_age_tables);
|
||||
FREE_C_HEAP_ARRAY(AgeTable*, _local_age_table);
|
||||
CENSUS_NOISE(FREE_C_HEAP_ARRAY(ShenandoahNoiseStats, _local_noise));
|
||||
}
|
||||
}
|
||||
@@ -146,31 +142,37 @@ void ShenandoahAgeCensus::prepare_for_census_update() {
|
||||
if (++_epoch >= MAX_SNAPSHOTS) {
|
||||
_epoch=0;
|
||||
}
|
||||
_global_age_tables[_epoch]->clear();
|
||||
_global_age_table[_epoch]->clear();
|
||||
CENSUS_NOISE(_global_noise[_epoch].clear();)
|
||||
}
|
||||
|
||||
// Update the census data from appropriate sources,
|
||||
// and compute the new tenuring threshold.
|
||||
void ShenandoahAgeCensus::update_census(size_t age0_pop) {
|
||||
void ShenandoahAgeCensus::update_census(size_t age0_pop, AgeTable* pv1, AgeTable* pv2) {
|
||||
prepare_for_census_update();
|
||||
assert(ShenandoahGenerationalAdaptiveTenuring, "Only update census when adaptive tenuring is enabled");
|
||||
assert(_global_age_tables[_epoch]->is_clear(), "Dirty decks");
|
||||
assert(_global_age_table[_epoch]->is_clear(), "Dirty decks");
|
||||
CENSUS_NOISE(assert(_global_noise[_epoch].is_clear(), "Dirty decks");)
|
||||
if (ShenandoahGenerationalAdaptiveTenuring) {
|
||||
assert(pv1 == nullptr && pv2 == nullptr, "Error, check caller");
|
||||
// Seed cohort 0 with population that may have been missed during
|
||||
// regular census.
|
||||
_global_age_table[_epoch]->add(0u, age0_pop);
|
||||
|
||||
// Seed cohort 0 with population that may have been missed during
|
||||
// regular census.
|
||||
_global_age_tables[_epoch]->add(0u, age0_pop);
|
||||
|
||||
// Merge data from local age tables into the global age table for the epoch,
|
||||
// clearing the local tables.
|
||||
for (uint i = 0; i < _max_workers; i++) {
|
||||
// age stats
|
||||
_global_age_tables[_epoch]->merge(_local_age_tables[i]);
|
||||
_local_age_tables[i]->clear(); // clear for next census
|
||||
// Merge noise stats
|
||||
CENSUS_NOISE(_global_noise[_epoch].merge(_local_noise[i]);)
|
||||
CENSUS_NOISE(_local_noise[i].clear();)
|
||||
// Merge data from local age tables into the global age table for the epoch,
|
||||
// clearing the local tables.
|
||||
for (uint i = 0; i < _max_workers; i++) {
|
||||
// age stats
|
||||
_global_age_table[_epoch]->merge(_local_age_table[i]);
|
||||
_local_age_table[i]->clear(); // clear for next census
|
||||
// Merge noise stats
|
||||
CENSUS_NOISE(_global_noise[_epoch].merge(_local_noise[i]);)
|
||||
CENSUS_NOISE(_local_noise[i].clear();)
|
||||
}
|
||||
} else {
|
||||
// census during evac
|
||||
assert(pv1 != nullptr && pv2 != nullptr, "Error, check caller");
|
||||
_global_age_table[_epoch]->merge(pv1);
|
||||
_global_age_table[_epoch]->merge(pv2);
|
||||
}
|
||||
|
||||
update_tenuring_threshold();
|
||||
@@ -186,7 +188,7 @@ void ShenandoahAgeCensus::update_census(size_t age0_pop) {
|
||||
void ShenandoahAgeCensus::reset_global() {
|
||||
assert(_epoch < MAX_SNAPSHOTS, "Out of bounds");
|
||||
for (uint i = 0; i < MAX_SNAPSHOTS; i++) {
|
||||
_global_age_tables[i]->clear();
|
||||
_global_age_table[i]->clear();
|
||||
CENSUS_NOISE(_global_noise[i].clear();)
|
||||
}
|
||||
_epoch = MAX_SNAPSHOTS;
|
||||
@@ -196,11 +198,11 @@ void ShenandoahAgeCensus::reset_global() {
|
||||
// Reset the local age tables, clearing any partial census.
|
||||
void ShenandoahAgeCensus::reset_local() {
|
||||
if (!ShenandoahGenerationalAdaptiveTenuring) {
|
||||
assert(_local_age_tables == nullptr, "Error");
|
||||
assert(_local_age_table == nullptr, "Error");
|
||||
return;
|
||||
}
|
||||
for (uint i = 0; i < _max_workers; i++) {
|
||||
_local_age_tables[i]->clear();
|
||||
_local_age_table[i]->clear();
|
||||
CENSUS_NOISE(_local_noise[i].clear();)
|
||||
}
|
||||
}
|
||||
@@ -210,7 +212,7 @@ void ShenandoahAgeCensus::reset_local() {
|
||||
bool ShenandoahAgeCensus::is_clear_global() {
|
||||
assert(_epoch < MAX_SNAPSHOTS, "Out of bounds");
|
||||
for (uint i = 0; i < MAX_SNAPSHOTS; i++) {
|
||||
bool clear = _global_age_tables[i]->is_clear();
|
||||
bool clear = _global_age_table[i]->is_clear();
|
||||
CENSUS_NOISE(clear |= _global_noise[i].is_clear();)
|
||||
if (!clear) {
|
||||
return false;
|
||||
@@ -222,11 +224,11 @@ bool ShenandoahAgeCensus::is_clear_global() {
|
||||
// Is local census information clear?
|
||||
bool ShenandoahAgeCensus::is_clear_local() {
|
||||
if (!ShenandoahGenerationalAdaptiveTenuring) {
|
||||
assert(_local_age_tables == nullptr, "Error");
|
||||
assert(_local_age_table == nullptr, "Error");
|
||||
return true;
|
||||
}
|
||||
for (uint i = 0; i < _max_workers; i++) {
|
||||
bool clear = _local_age_tables[i]->is_clear();
|
||||
bool clear = _local_age_table[i]->is_clear();
|
||||
CENSUS_NOISE(clear |= _local_noise[i].is_clear();)
|
||||
if (!clear) {
|
||||
return false;
|
||||
@@ -238,7 +240,7 @@ bool ShenandoahAgeCensus::is_clear_local() {
|
||||
size_t ShenandoahAgeCensus::get_all_ages(uint snap) {
|
||||
assert(snap < MAX_SNAPSHOTS, "Out of bounds");
|
||||
size_t pop = 0;
|
||||
const AgeTable* pv = _global_age_tables[snap];
|
||||
const AgeTable* pv = _global_age_table[snap];
|
||||
for (uint i = 0; i < MAX_COHORTS; i++) {
|
||||
pop += pv->sizes[i];
|
||||
}
|
||||
@@ -258,11 +260,13 @@ void ShenandoahAgeCensus::update_total() {
|
||||
#endif // !PRODUCT
|
||||
|
||||
void ShenandoahAgeCensus::update_tenuring_threshold() {
|
||||
assert(ShenandoahGenerationalAdaptiveTenuring, "Only update when adaptive tenuring is enabled");
|
||||
uint tt = compute_tenuring_threshold();
|
||||
assert(tt <= MAX_COHORTS, "Out of bounds");
|
||||
_tenuring_threshold[_epoch] = tt;
|
||||
|
||||
if (!ShenandoahGenerationalAdaptiveTenuring) {
|
||||
_tenuring_threshold[_epoch] = InitialTenuringThreshold;
|
||||
} else {
|
||||
uint tt = compute_tenuring_threshold();
|
||||
assert(tt <= MAX_COHORTS, "Out of bounds");
|
||||
_tenuring_threshold[_epoch] = tt;
|
||||
}
|
||||
print();
|
||||
log_info(gc, age)("New tenuring threshold %zu (min %zu, max %zu)",
|
||||
(uintx) _tenuring_threshold[_epoch], ShenandoahGenerationalMinTenuringAge, ShenandoahGenerationalMaxTenuringAge);
|
||||
@@ -292,8 +296,8 @@ uint ShenandoahAgeCensus::compute_tenuring_threshold() {
|
||||
const uint prev_epoch = cur_epoch > 0 ? cur_epoch - 1 : markWord::max_age;
|
||||
|
||||
// Current and previous population vectors in ring
|
||||
const AgeTable* cur_pv = _global_age_tables[cur_epoch];
|
||||
const AgeTable* prev_pv = _global_age_tables[prev_epoch];
|
||||
const AgeTable* cur_pv = _global_age_table[cur_epoch];
|
||||
const AgeTable* prev_pv = _global_age_table[prev_epoch];
|
||||
uint upper_bound = ShenandoahGenerationalMaxTenuringAge;
|
||||
const uint prev_tt = previous_tenuring_threshold();
|
||||
if (ShenandoahGenerationalCensusIgnoreOlderCohorts && prev_tt > 0) {
|
||||
@@ -368,8 +372,8 @@ void ShenandoahAgeCensus::print() {
|
||||
const uint cur_epoch = _epoch;
|
||||
const uint prev_epoch = cur_epoch > 0 ? cur_epoch - 1: markWord::max_age;
|
||||
|
||||
const AgeTable* cur_pv = _global_age_tables[cur_epoch];
|
||||
const AgeTable* prev_pv = _global_age_tables[prev_epoch];
|
||||
const AgeTable* cur_pv = _global_age_table[cur_epoch];
|
||||
const AgeTable* prev_pv = _global_age_table[prev_epoch];
|
||||
|
||||
const uint tt = tenuring_threshold();
|
||||
|
||||
|
||||
@@ -97,8 +97,8 @@ struct ShenandoahNoiseStats {
|
||||
// once the per-worker data is consolidated into the appropriate population vector
|
||||
// per minor collection. The _local_age_table is thus C x N, for N GC workers.
|
||||
class ShenandoahAgeCensus: public CHeapObj<mtGC> {
|
||||
AgeTable** _global_age_tables; // Global age tables used for adapting tenuring threshold, one per snapshot
|
||||
AgeTable** _local_age_tables; // Local scratch age tables to track object ages, one per worker
|
||||
AgeTable** _global_age_table; // Global age table used for adapting tenuring threshold, one per snapshot
|
||||
AgeTable** _local_age_table; // Local scratch age tables to track object ages, one per worker
|
||||
|
||||
#ifdef SHENANDOAH_CENSUS_NOISE
|
||||
ShenandoahNoiseStats* _global_noise; // Noise stats, one per snapshot
|
||||
@@ -175,7 +175,7 @@ class ShenandoahAgeCensus: public CHeapObj<mtGC> {
|
||||
// Return the local age table (population vector) for worker_id.
|
||||
// Only used in the case of ShenandoahGenerationalAdaptiveTenuring
|
||||
AgeTable* get_local_age_table(uint worker_id) const {
|
||||
return _local_age_tables[worker_id];
|
||||
return _local_age_table[worker_id];
|
||||
}
|
||||
|
||||
// Return the most recently computed tenuring threshold.
|
||||
@@ -209,7 +209,11 @@ class ShenandoahAgeCensus: public CHeapObj<mtGC> {
|
||||
// age0_pop is the population of Cohort 0 that may have been missed in
|
||||
// the regular census during the marking cycle, corresponding to objects
|
||||
// allocated when the concurrent marking was in progress.
|
||||
void update_census(size_t age0_pop);
|
||||
// Optional parameters, pv1 and pv2 are population vectors that together
|
||||
// provide object census data (only) for the case when
|
||||
// ShenandoahGenerationalCensusAtEvac. In this case, the age0_pop
|
||||
// is 0, because the evacuated objects have all had their ages incremented.
|
||||
void update_census(size_t age0_pop, AgeTable* pv1 = nullptr, AgeTable* pv2 = nullptr);
|
||||
|
||||
// Reset the epoch, clearing accumulated census history
|
||||
// Note: this isn't currently used, but reserved for planned
|
||||
|
||||
@@ -201,8 +201,26 @@ void ShenandoahControlThread::run_service() {
|
||||
heuristics->clear_metaspace_oom();
|
||||
}
|
||||
|
||||
// Manage and print gc stats
|
||||
heap->process_gc_stats();
|
||||
// Commit worker statistics to cycle data
|
||||
heap->phase_timings()->flush_par_workers_to_cycle();
|
||||
|
||||
// Print GC stats for current cycle
|
||||
{
|
||||
LogTarget(Info, gc, stats) lt;
|
||||
if (lt.is_enabled()) {
|
||||
ResourceMark rm;
|
||||
LogStream ls(lt);
|
||||
heap->phase_timings()->print_cycle_on(&ls);
|
||||
if (ShenandoahEvacTracking) {
|
||||
ShenandoahEvacuationTracker* evac_tracker = heap->evac_tracker();
|
||||
ShenandoahCycleStats evac_stats = evac_tracker->flush_cycle_to_global();
|
||||
evac_tracker->print_evacuations_on(&ls, &evac_stats.workers, &evac_stats.mutators);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Commit statistics to globals
|
||||
heap->phase_timings()->flush_cycle_to_global();
|
||||
|
||||
// Print Metaspace change following GC (if logging is enabled).
|
||||
MetaspaceUtils::print_metaspace_change(meta_sizes);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gc/shenandoah/shenandoahAgeCensus.hpp"
|
||||
#include "gc/shenandoah/shenandoahEvacTracker.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
|
||||
#include "gc/shenandoah/shenandoahThreadLocalData.hpp"
|
||||
@@ -43,6 +44,19 @@ ShenandoahEvacuationStats::ShenandoahEvacuations* ShenandoahEvacuationStats::get
|
||||
return &_old;
|
||||
}
|
||||
|
||||
ShenandoahEvacuationStats::ShenandoahEvacuationStats()
|
||||
: _use_age_table(!ShenandoahGenerationalAdaptiveTenuring),
|
||||
_age_table(nullptr) {
|
||||
if (_use_age_table) {
|
||||
_age_table = new AgeTable(false);
|
||||
}
|
||||
}
|
||||
|
||||
AgeTable* ShenandoahEvacuationStats::age_table() const {
|
||||
assert(_use_age_table, "Don't call");
|
||||
return _age_table;
|
||||
}
|
||||
|
||||
void ShenandoahEvacuationStats::begin_evacuation(size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) {
|
||||
ShenandoahEvacuations* category = get_category(from, to);
|
||||
category->_evacuations_attempted++;
|
||||
@@ -56,16 +70,31 @@ void ShenandoahEvacuationStats::end_evacuation(size_t bytes, ShenandoahAffiliati
|
||||
category->_bytes_completed += bytes;
|
||||
}
|
||||
|
||||
void ShenandoahEvacuationStats::record_age(size_t bytes, uint age) {
|
||||
assert(_use_age_table, "Don't call!");
|
||||
if (age <= markWord::max_age) { // Filter age sentinel.
|
||||
_age_table->add(age, bytes >> LogBytesPerWord);
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahEvacuationStats::accumulate(const ShenandoahEvacuationStats* other) {
|
||||
_young.accumulate(other->_young);
|
||||
_old.accumulate(other->_old);
|
||||
_promotion.accumulate(other->_promotion);
|
||||
|
||||
if (_use_age_table) {
|
||||
_age_table->merge(other->age_table());
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahEvacuationStats::reset() {
|
||||
_young.reset();
|
||||
_old.reset();
|
||||
_promotion.reset();
|
||||
|
||||
if (_use_age_table) {
|
||||
_age_table->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahEvacuationStats::ShenandoahEvacuations::print_on(outputStream* st) const {
|
||||
@@ -83,6 +112,10 @@ void ShenandoahEvacuationStats::print_on(outputStream* st) const {
|
||||
st->print("Promotion: "); _promotion.print_on(st);
|
||||
st->print("Old: "); _old.print_on(st);
|
||||
}
|
||||
|
||||
if (_use_age_table) {
|
||||
_age_table->print_on(st);
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahEvacuationTracker::print_global_on(outputStream* st) {
|
||||
@@ -92,13 +125,28 @@ void ShenandoahEvacuationTracker::print_global_on(outputStream* st) {
|
||||
void ShenandoahEvacuationTracker::print_evacuations_on(outputStream* st,
|
||||
ShenandoahEvacuationStats* workers,
|
||||
ShenandoahEvacuationStats* mutators) {
|
||||
assert(ShenandoahEvacTracking, "Only when evac tracking is enabled");
|
||||
st->print_cr("Workers: ");
|
||||
workers->print_on(st);
|
||||
st->cr();
|
||||
st->print_cr("Mutators: ");
|
||||
mutators->print_on(st);
|
||||
st->cr();
|
||||
if (ShenandoahEvacTracking) {
|
||||
st->print_cr("Workers: ");
|
||||
workers->print_on(st);
|
||||
st->cr();
|
||||
st->print_cr("Mutators: ");
|
||||
mutators->print_on(st);
|
||||
st->cr();
|
||||
}
|
||||
|
||||
ShenandoahHeap* heap = ShenandoahHeap::heap();
|
||||
if (heap->mode()->is_generational()) {
|
||||
AgeTable young_region_ages(false);
|
||||
for (uint i = 0; i < heap->num_regions(); ++i) {
|
||||
ShenandoahHeapRegion* r = heap->get_region(i);
|
||||
if (r->is_young()) {
|
||||
young_region_ages.add(r->age(), r->get_live_data_words());
|
||||
}
|
||||
}
|
||||
st->print("Young regions: ");
|
||||
young_region_ages.print_on(st);
|
||||
st->cr();
|
||||
}
|
||||
}
|
||||
|
||||
class ShenandoahStatAggregator : public ThreadClosure {
|
||||
@@ -125,6 +173,15 @@ ShenandoahCycleStats ShenandoahEvacuationTracker::flush_cycle_to_global() {
|
||||
_mutators_global.accumulate(&mutators);
|
||||
_workers_global.accumulate(&workers);
|
||||
|
||||
if (!ShenandoahGenerationalAdaptiveTenuring) {
|
||||
// Ingest mutator & worker collected population vectors into the heap's
|
||||
// global census data, and use it to compute an appropriate tenuring threshold
|
||||
// for use in the next cycle.
|
||||
// The first argument is used for any age 0 cohort population that we may otherwise have
|
||||
// missed during the census. This is non-zero only when census happens at marking.
|
||||
ShenandoahGenerationalHeap::heap()->age_census()->update_census(0, mutators.age_table(), workers.age_table());
|
||||
}
|
||||
|
||||
return {workers, mutators};
|
||||
}
|
||||
|
||||
@@ -135,3 +192,7 @@ void ShenandoahEvacuationTracker::begin_evacuation(Thread* thread, size_t bytes,
|
||||
void ShenandoahEvacuationTracker::end_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) {
|
||||
ShenandoahThreadLocalData::end_evacuation(thread, bytes, from, to);
|
||||
}
|
||||
|
||||
void ShenandoahEvacuationTracker::record_age(Thread* thread, size_t bytes, uint age) {
|
||||
ShenandoahThreadLocalData::record_age(thread, bytes, age);
|
||||
}
|
||||
|
||||
@@ -66,12 +66,20 @@ private:
|
||||
ShenandoahEvacuations _old;
|
||||
ShenandoahEvacuations _promotion;
|
||||
|
||||
bool _use_age_table;
|
||||
AgeTable* _age_table;
|
||||
|
||||
public:
|
||||
ShenandoahEvacuationStats();
|
||||
|
||||
AgeTable* age_table() const;
|
||||
|
||||
// Record that the current thread is attempting to copy this many bytes.
|
||||
void begin_evacuation(size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to);
|
||||
|
||||
// Record that the current thread has completed copying this many bytes.
|
||||
void end_evacuation(size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to);
|
||||
void record_age(size_t bytes, uint age);
|
||||
|
||||
void print_on(outputStream* st) const;
|
||||
void accumulate(const ShenandoahEvacuationStats* other);
|
||||
@@ -98,6 +106,7 @@ public:
|
||||
// Multiple threads may attempt to evacuate the same object, but only the successful thread will end the evacuation.
|
||||
// Evacuations that were begun, but not ended are considered 'abandoned'.
|
||||
void end_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to);
|
||||
void record_age(Thread* thread, size_t bytes, uint age);
|
||||
|
||||
void print_global_on(outputStream* st);
|
||||
void print_evacuations_on(outputStream* st,
|
||||
|
||||
@@ -201,24 +201,6 @@ ShenandoahGenerationalControlThread::GCMode ShenandoahGenerationalControlThread:
|
||||
return request.generation->is_old() ? servicing_old : concurrent_normal;
|
||||
}
|
||||
|
||||
void ShenandoahGenerationalControlThread::maybe_print_young_region_ages() const {
|
||||
LogTarget(Debug, gc, age) lt;
|
||||
if (lt.is_enabled()) {
|
||||
LogStream ls(lt);
|
||||
AgeTable young_region_ages(false);
|
||||
for (uint i = 0; i < _heap->num_regions(); ++i) {
|
||||
const ShenandoahHeapRegion* r = _heap->get_region(i);
|
||||
if (r->is_young()) {
|
||||
young_region_ages.add(r->age(), r->get_live_data_words());
|
||||
}
|
||||
}
|
||||
|
||||
ls.print("Young regions: ");
|
||||
young_region_ages.print_on(&ls);
|
||||
ls.cr();
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahGenerationalControlThread::maybe_set_aging_cycle() {
|
||||
if (_age_period-- == 0) {
|
||||
_heap->set_aging_cycle(true);
|
||||
@@ -316,11 +298,7 @@ void ShenandoahGenerationalControlThread::run_gc_cycle(const ShenandoahGCRequest
|
||||
_heap->global_generation()->heuristics()->clear_metaspace_oom();
|
||||
}
|
||||
|
||||
// Manage and print gc stats
|
||||
_heap->process_gc_stats();
|
||||
|
||||
// Print table for young region ages if log is enabled
|
||||
maybe_print_young_region_ages();
|
||||
process_phase_timings();
|
||||
|
||||
// Print Metaspace change following GC (if logging is enabled).
|
||||
MetaspaceUtils::print_metaspace_change(meta_sizes);
|
||||
@@ -339,6 +317,29 @@ void ShenandoahGenerationalControlThread::run_gc_cycle(const ShenandoahGCRequest
|
||||
gc_mode_name(gc_mode()), GCCause::to_string(request.cause), request.generation->name(), GCCause::to_string(_heap->cancelled_cause()));
|
||||
}
|
||||
|
||||
void ShenandoahGenerationalControlThread::process_phase_timings() const {
|
||||
// Commit worker statistics to cycle data
|
||||
_heap->phase_timings()->flush_par_workers_to_cycle();
|
||||
|
||||
ShenandoahEvacuationTracker* evac_tracker = _heap->evac_tracker();
|
||||
ShenandoahCycleStats evac_stats = evac_tracker->flush_cycle_to_global();
|
||||
|
||||
// Print GC stats for current cycle
|
||||
{
|
||||
LogTarget(Info, gc, stats) lt;
|
||||
if (lt.is_enabled()) {
|
||||
ResourceMark rm;
|
||||
LogStream ls(lt);
|
||||
_heap->phase_timings()->print_cycle_on(&ls);
|
||||
evac_tracker->print_evacuations_on(&ls, &evac_stats.workers,
|
||||
&evac_stats.mutators);
|
||||
}
|
||||
}
|
||||
|
||||
// Commit statistics to globals
|
||||
_heap->phase_timings()->flush_cycle_to_global();
|
||||
}
|
||||
|
||||
// Young and old concurrent cycles are initiated by the regulator. Implicit
|
||||
// and explicit GC requests are handled by the controller thread and always
|
||||
// run a global cycle (which is concurrent by default, but may be overridden
|
||||
@@ -416,7 +417,7 @@ void ShenandoahGenerationalControlThread::service_concurrent_old_cycle(const She
|
||||
set_gc_mode(bootstrapping_old);
|
||||
young_generation->set_old_gen_task_queues(old_generation->task_queues());
|
||||
service_concurrent_cycle(young_generation, request.cause, true);
|
||||
_heap->process_gc_stats();
|
||||
process_phase_timings();
|
||||
if (_heap->cancelled_gc()) {
|
||||
// Young generation bootstrap cycle has failed. Concurrent mark for old generation
|
||||
// is going to resume after degenerated bootstrap cycle completes.
|
||||
|
||||
@@ -129,6 +129,9 @@ private:
|
||||
// Returns true if the old generation marking was interrupted to allow a young cycle.
|
||||
bool preempt_old_marking(ShenandoahGeneration* generation);
|
||||
|
||||
// Flushes cycle timings to global timings and prints the phase timings for the last completed cycle.
|
||||
void process_phase_timings() const;
|
||||
|
||||
// Set the gc mode and post a notification if it has changed. The overloaded variant should be used
|
||||
// when the _control_lock is already held.
|
||||
void set_gc_mode(GCMode new_mode);
|
||||
@@ -157,9 +160,6 @@ private:
|
||||
GCMode prepare_for_allocation_failure_gc(ShenandoahGCRequest &request);
|
||||
GCMode prepare_for_explicit_gc(ShenandoahGCRequest &request) const;
|
||||
GCMode prepare_for_concurrent_gc(const ShenandoahGCRequest &request) const;
|
||||
|
||||
// Print table for young region ages if log is enabled
|
||||
void maybe_print_young_region_ages() const;
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALCONTROLTHREAD_HPP
|
||||
|
||||
@@ -360,6 +360,11 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, Shena
|
||||
// When copying to the old generation above, we don't care
|
||||
// about recording object age in the census stats.
|
||||
assert(target_gen == YOUNG_GENERATION, "Error");
|
||||
// We record this census only when simulating pre-adaptive tenuring behavior, or
|
||||
// when we have been asked to record the census at evacuation rather than at mark
|
||||
if (!ShenandoahGenerationalAdaptiveTenuring) {
|
||||
evac_tracker()->record_age(thread, size * HeapWordSize, ShenandoahHeap::get_object_age(copy_val));
|
||||
}
|
||||
}
|
||||
shenandoah_assert_correct(nullptr, copy_val);
|
||||
return copy_val;
|
||||
|
||||
@@ -1441,27 +1441,6 @@ void ShenandoahHeap::print_heap_regions_on(outputStream* st) const {
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahHeap::process_gc_stats() const {
|
||||
// Commit worker statistics to cycle data
|
||||
phase_timings()->flush_par_workers_to_cycle();
|
||||
|
||||
// Print GC stats for current cycle
|
||||
LogTarget(Info, gc, stats) lt;
|
||||
if (lt.is_enabled()) {
|
||||
ResourceMark rm;
|
||||
LogStream ls(lt);
|
||||
phase_timings()->print_cycle_on(&ls);
|
||||
if (ShenandoahEvacTracking) {
|
||||
ShenandoahCycleStats evac_stats = evac_tracker()->flush_cycle_to_global();
|
||||
evac_tracker()->print_evacuations_on(&ls, &evac_stats.workers,
|
||||
&evac_stats.mutators);
|
||||
}
|
||||
}
|
||||
|
||||
// Commit statistics to globals
|
||||
phase_timings()->flush_cycle_to_global();
|
||||
}
|
||||
|
||||
size_t ShenandoahHeap::trash_humongous_region_at(ShenandoahHeapRegion* start) const {
|
||||
assert(start->is_humongous_start(), "reclaim regions starting with the first one");
|
||||
assert(!start->has_live(), "liveness must be zero");
|
||||
|
||||
@@ -206,12 +206,9 @@ public:
|
||||
void initialize_serviceability() override;
|
||||
|
||||
void print_heap_on(outputStream* st) const override;
|
||||
void print_gc_on(outputStream* st) const override;
|
||||
void print_gc_on(outputStream *st) const override;
|
||||
void print_heap_regions_on(outputStream* st) const;
|
||||
|
||||
// Flushes cycle timings to global timings and prints the phase timings for the last completed cycle.
|
||||
void process_gc_stats() const;
|
||||
|
||||
void prepare_for_verify() override;
|
||||
void verify(VerifyOption vo) override;
|
||||
|
||||
|
||||
@@ -166,6 +166,10 @@ public:
|
||||
data(thread)->_evacuation_stats->end_evacuation(bytes, from, to);
|
||||
}
|
||||
|
||||
static void record_age(Thread* thread, size_t bytes, uint age) {
|
||||
data(thread)->_evacuation_stats->record_age(bytes, age);
|
||||
}
|
||||
|
||||
static ShenandoahEvacuationStats* evacuation_stats(Thread* thread) {
|
||||
return data(thread)->_evacuation_stats;
|
||||
}
|
||||
|
||||
@@ -618,7 +618,7 @@ void CompileTrainingData::verify(bool verify_dep_counter) {
|
||||
for (int i = 0; i < init_dep_count(); i++) {
|
||||
KlassTrainingData* ktd = init_dep(i);
|
||||
if (ktd->has_holder() && ktd->holder()->defined_by_other_loaders()) {
|
||||
LogStreamHandle(Info, training) log;
|
||||
LogStreamHandle(Warning, training) log;
|
||||
if (log.is_enabled()) {
|
||||
ResourceMark rm;
|
||||
log.print("CTD "); print_value_on(&log);
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "jvm_io.h"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/stringUtils.hpp"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2024, 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
|
||||
|
||||
@@ -4180,10 +4180,6 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
|
||||
radix = 10;
|
||||
|
||||
if (fitsIntoLong()) {
|
||||
return Long.toString(longValue(), radix);
|
||||
}
|
||||
|
||||
BigInteger abs = this.abs();
|
||||
|
||||
// Ensure buffer capacity sufficient to contain string representation
|
||||
@@ -5125,16 +5121,12 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
* @since 1.8
|
||||
*/
|
||||
public long longValueExact() {
|
||||
if (fitsIntoLong())
|
||||
if (mag.length <= 2 && bitLength() < Long.SIZE)
|
||||
return longValue();
|
||||
|
||||
throw new ArithmeticException("BigInteger out of long range");
|
||||
}
|
||||
|
||||
private boolean fitsIntoLong() {
|
||||
return mag.length <= 2 && bitLength() < Long.SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this {@code BigInteger} to an {@code int}, checking
|
||||
* for lost information. If the value of this {@code BigInteger}
|
||||
|
||||
@@ -46,7 +46,6 @@ import java.nio.charset.StandardCharsets;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.math.FloatingDecimal;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
/**
|
||||
* Digit List. Private to DecimalFormat.
|
||||
@@ -109,6 +108,7 @@ final class DigitList implements Cloneable {
|
||||
public int count = 0;
|
||||
public byte[] digits = new byte[MAX_COUNT];
|
||||
|
||||
private byte[] data;
|
||||
private RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||
private boolean isNegative = false;
|
||||
|
||||
@@ -320,18 +320,15 @@ final class DigitList implements Cloneable {
|
||||
* fractional digits to be converted. If false, total digits.
|
||||
*/
|
||||
void set(boolean isNegative, double source, int maximumDigits, boolean fixedPoint) {
|
||||
assert Double.isFinite(source);
|
||||
|
||||
FloatingDecimal.BinaryToASCIIConverter fdConverter =
|
||||
FloatingDecimal.getBinaryToASCIIConverter(source, COMPAT);
|
||||
boolean hasBeenRoundedUp = fdConverter.digitsRoundedUp();
|
||||
boolean valueExactAsDecimal = fdConverter.decimalDigitsExact();
|
||||
assert !fdConverter.isExceptional();
|
||||
|
||||
count = fdConverter.getDigits(digits);
|
||||
|
||||
int exp = fdConverter.getDecimalExponent() - count;
|
||||
|
||||
set(isNegative, exp,
|
||||
byte[] chars = getDataChars(26);
|
||||
int len = fdConverter.getChars(chars);
|
||||
set(isNegative, chars, len,
|
||||
hasBeenRoundedUp, valueExactAsDecimal,
|
||||
maximumDigits, fixedPoint);
|
||||
}
|
||||
@@ -343,18 +340,44 @@ final class DigitList implements Cloneable {
|
||||
* @param valueExactAsDecimal whether or not collected digits provide
|
||||
* an exact decimal representation of the value.
|
||||
*/
|
||||
private void set(boolean isNegative, int exp,
|
||||
private void set(boolean isNegative, byte[] source, int len,
|
||||
boolean roundedUp, boolean valueExactAsDecimal,
|
||||
int maximumDigits, boolean fixedPoint) {
|
||||
|
||||
this.isNegative = isNegative;
|
||||
|
||||
if (!nonZeroAfterIndex(0)) {
|
||||
count = 0;
|
||||
decimalAt = 0;
|
||||
return;
|
||||
decimalAt = -1;
|
||||
count = 0;
|
||||
int exponent = 0;
|
||||
// Number of zeros between decimal point and first non-zero digit after
|
||||
// decimal point, for numbers < 1.
|
||||
int leadingZerosAfterDecimal = 0;
|
||||
boolean nonZeroDigitSeen = false;
|
||||
|
||||
for (int i = 0; i < len; ) {
|
||||
byte c = source[i++];
|
||||
if (c == '.') {
|
||||
decimalAt = count;
|
||||
} else if (c == 'e' || c == 'E') {
|
||||
exponent = parseInt(source, i, len);
|
||||
break;
|
||||
} else {
|
||||
if (!nonZeroDigitSeen) {
|
||||
nonZeroDigitSeen = (c != '0');
|
||||
if (!nonZeroDigitSeen && decimalAt != -1)
|
||||
++leadingZerosAfterDecimal;
|
||||
}
|
||||
if (nonZeroDigitSeen) {
|
||||
digits[count++] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (decimalAt == -1) {
|
||||
decimalAt = count;
|
||||
}
|
||||
if (nonZeroDigitSeen) {
|
||||
decimalAt += exponent - leadingZerosAfterDecimal;
|
||||
}
|
||||
decimalAt = count + exp;
|
||||
|
||||
if (fixedPoint) {
|
||||
// The negative of the exponent represents the number of leading
|
||||
@@ -646,13 +669,13 @@ final class DigitList implements Cloneable {
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
void set(boolean isNegative, BigDecimal source, int maximumDigits, boolean fixedPoint) {
|
||||
String s = source.unscaledValue().toString();
|
||||
int len = s.length();
|
||||
String s = source.toString();
|
||||
extendDigits(s.length());
|
||||
|
||||
extendDigits(len);
|
||||
s.getBytes(0, len, digits, 0);
|
||||
count = len;
|
||||
set(isNegative, -source.scale(),
|
||||
int len = s.length();
|
||||
byte[] chars = getDataChars(len);
|
||||
s.getBytes(0, len, chars, 0);
|
||||
set(isNegative, chars, len,
|
||||
false, true,
|
||||
maximumDigits, fixedPoint);
|
||||
}
|
||||
@@ -722,7 +745,14 @@ final class DigitList implements Cloneable {
|
||||
public Object clone() {
|
||||
try {
|
||||
DigitList other = (DigitList) super.clone();
|
||||
other.digits = digits.clone();
|
||||
byte[] newDigits = new byte[digits.length];
|
||||
System.arraycopy(digits, 0, newDigits, 0, digits.length);
|
||||
other.digits = newDigits;
|
||||
|
||||
// Data does not need to be copied because it does
|
||||
// not carry significant information. It will be recreated on demand.
|
||||
// Setting it to null is needed to avoid sharing across clones.
|
||||
other.data = null;
|
||||
|
||||
return other;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
@@ -730,8 +760,29 @@ final class DigitList implements Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
private static int parseInt(byte[] str, int offset, int strLen) {
|
||||
byte c;
|
||||
boolean positive = true;
|
||||
if ((c = str[offset]) == '-') {
|
||||
positive = false;
|
||||
offset++;
|
||||
} else if (c == '+') {
|
||||
offset++;
|
||||
}
|
||||
|
||||
int value = 0;
|
||||
while (offset < strLen) {
|
||||
c = str[offset++];
|
||||
if (c >= '0' && c <= '9') {
|
||||
value = value * 10 + (c - '0');
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return positive ? value : -value;
|
||||
}
|
||||
|
||||
// The digit part of -9223372036854775808L
|
||||
@Stable
|
||||
private static final byte[] LONG_MIN_REP = "9223372036854775808".getBytes(StandardCharsets.ISO_8859_1);
|
||||
|
||||
public String toString() {
|
||||
@@ -747,4 +798,11 @@ final class DigitList implements Cloneable {
|
||||
digits = new byte[len];
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getDataChars(int length) {
|
||||
if (data == null || data.length < length) {
|
||||
data = new byte[length];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,12 +515,12 @@ public class Cipher {
|
||||
* transformation
|
||||
*
|
||||
* @throws NoSuchAlgorithmException if {@code transformation}
|
||||
* is {@code null}, empty or in an invalid format;
|
||||
* or if a {@code CipherSpi} implementation is not found or
|
||||
* is found but does not support the mode
|
||||
* is {@code null}, empty, in an invalid format,
|
||||
* or if no provider supports a {@code CipherSpi}
|
||||
* implementation for the specified algorithm
|
||||
*
|
||||
* @throws NoSuchPaddingException if a {@code CipherSpi} implementation
|
||||
* is found but does not support the padding scheme
|
||||
* @throws NoSuchPaddingException if {@code transformation}
|
||||
* contains a padding scheme that is not available
|
||||
*
|
||||
* @see java.security.Provider
|
||||
*/
|
||||
@@ -573,12 +573,8 @@ public class Cipher {
|
||||
failure = e;
|
||||
}
|
||||
}
|
||||
if (failure instanceof NoSuchPaddingException nspe) {
|
||||
throw nspe;
|
||||
}
|
||||
throw new NoSuchAlgorithmException
|
||||
("Cannot find any provider supporting " + transformation,
|
||||
failure);
|
||||
("Cannot find any provider supporting " + transformation, failure);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -586,8 +582,8 @@ public class Cipher {
|
||||
* transformation.
|
||||
*
|
||||
* <p> A new {@code Cipher} object encapsulating the
|
||||
* {@code CipherSpi} implementation from the specified {@code provider}
|
||||
* is returned. The specified {@code provider} must be registered
|
||||
* {@code CipherSpi} implementation from the specified provider
|
||||
* is returned. The specified provider must be registered
|
||||
* in the security provider list.
|
||||
*
|
||||
* <p> Note that the list of registered providers may be retrieved via
|
||||
@@ -629,16 +625,15 @@ public class Cipher {
|
||||
* is {@code null} or empty
|
||||
*
|
||||
* @throws NoSuchAlgorithmException if {@code transformation}
|
||||
* is {@code null}, empty or in an invalid format;
|
||||
* or if a {@code CipherSpi} implementation from the specified
|
||||
* {@code provider} is not found or is found but does not support
|
||||
* the mode
|
||||
* is {@code null}, empty, in an invalid format,
|
||||
* or if a {@code CipherSpi} implementation for the
|
||||
* specified algorithm is not available from the specified
|
||||
* provider
|
||||
*
|
||||
* @throws NoSuchPaddingException if a {@code CipherSpi} implementation
|
||||
* from the specified {@code provider} is found but does not
|
||||
* support the padding scheme
|
||||
* @throws NoSuchPaddingException if {@code transformation}
|
||||
* contains a padding scheme that is not available
|
||||
*
|
||||
* @throws NoSuchProviderException if the specified {@code provider} is not
|
||||
* @throws NoSuchProviderException if the specified provider is not
|
||||
* registered in the security provider list
|
||||
*
|
||||
* @see java.security.Provider
|
||||
@@ -711,14 +706,13 @@ public class Cipher {
|
||||
* is {@code null}
|
||||
*
|
||||
* @throws NoSuchAlgorithmException if {@code transformation}
|
||||
* is {@code null}, empty or in an invalid format;
|
||||
* or if a {@code CipherSpi} implementation from the specified
|
||||
* {@code provider} is not found or is found but does not support
|
||||
* the mode
|
||||
* is {@code null}, empty, in an invalid format,
|
||||
* or if a {@code CipherSpi} implementation for the
|
||||
* specified algorithm is not available from the specified
|
||||
* {@code provider} object
|
||||
*
|
||||
* @throws NoSuchPaddingException if a {@code CipherSpi} implementation
|
||||
* from the specified {@code provider} is found but does not
|
||||
* support the padding scheme
|
||||
* @throws NoSuchPaddingException if {@code transformation}
|
||||
* contains a padding scheme that is not available
|
||||
*
|
||||
* @see java.security.Provider
|
||||
*/
|
||||
|
||||
@@ -115,7 +115,7 @@ public class FloatingDecimal{
|
||||
* @param digits The digit array.
|
||||
* @return The number of valid digits copied into the array.
|
||||
*/
|
||||
int getDigits(byte[] digits);
|
||||
int getDigits(char[] digits);
|
||||
|
||||
/**
|
||||
* Indicates the sign of the value.
|
||||
@@ -173,7 +173,7 @@ public class FloatingDecimal{
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDigits(byte[] digits) {
|
||||
public int getDigits(char[] digits) {
|
||||
throw new IllegalArgumentException("Exceptional value does not have digits");
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ public class FloatingDecimal{
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDigits(byte[] digits) {
|
||||
public int getDigits(char[] digits) {
|
||||
System.arraycopy(this.digits, firstDigitIndex, digits, 0, this.nDigits);
|
||||
return this.nDigits;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2024, 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
|
||||
@@ -801,11 +801,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
|
||||
|
||||
// buffer this fragment
|
||||
if (hsf.handshakeType == SSLHandshake.FINISHED.id) {
|
||||
// Make sure it's not a retransmitted message
|
||||
if (hsf.recordEpoch > handshakeEpoch) {
|
||||
bufferedFragments.add(hsf);
|
||||
flightIsReady = holes.isEmpty();
|
||||
}
|
||||
// Need no status update.
|
||||
bufferedFragments.add(hsf);
|
||||
} else {
|
||||
bufferFragment(hsf);
|
||||
}
|
||||
|
||||
@@ -278,10 +278,8 @@ final class SessionTicketExtension {
|
||||
aad.putInt(keyID).put(compressed);
|
||||
c.updateAAD(aad);
|
||||
|
||||
// use getOutputSize to avoid a ShortBufferException
|
||||
// from providers that require oversized buffers. See JDK-8368514.
|
||||
ByteBuffer out = ByteBuffer.allocate(
|
||||
c.getOutputSize(data.remaining()));
|
||||
data.remaining() - GCM_TAG_LEN / 8);
|
||||
c.doFinal(data, out);
|
||||
out.flip();
|
||||
|
||||
@@ -293,7 +291,7 @@ final class SessionTicketExtension {
|
||||
return out;
|
||||
} catch (Exception e) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Decryption failed." + e);
|
||||
SSLLogger.fine("Decryption failed." + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -68,7 +67,7 @@ final class DesktopIntegration extends ShellCustomAction {
|
||||
private static final List<String> REPLACEMENT_STRING_IDS = List.of(
|
||||
COMMANDS_INSTALL, COMMANDS_UNINSTALL, SCRIPTS, COMMON_SCRIPTS);
|
||||
|
||||
private DesktopIntegration(BuildEnv env, LinuxPackage pkg, LinuxLauncher launcher) {
|
||||
private DesktopIntegration(BuildEnv env, LinuxPackage pkg, LinuxLauncher launcher) throws IOException {
|
||||
|
||||
associations = launcher.fileAssociations().stream().map(
|
||||
LinuxFileAssociation::create).toList();
|
||||
@@ -89,14 +88,10 @@ final class DesktopIntegration extends ShellCustomAction {
|
||||
// This is additional launcher with explicit `no icon` configuration.
|
||||
withDesktopFile = false;
|
||||
} else {
|
||||
try {
|
||||
if (curIconResource.get().saveToFile((Path)null) != OverridableResource.Source.DefaultResource) {
|
||||
// This launcher has custom icon configured.
|
||||
withDesktopFile = true;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
// Should never happen as `saveToFile((Path)null)` should not perform any actual I/O operations.
|
||||
throw new UncheckedIOException(ex);
|
||||
final Path nullPath = null;
|
||||
if (curIconResource.get().saveToFile(nullPath) != OverridableResource.Source.DefaultResource) {
|
||||
// This launcher has custom icon configured.
|
||||
withDesktopFile = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,13 +135,13 @@ final class DesktopIntegration extends ShellCustomAction {
|
||||
return (LinuxLauncher)v;
|
||||
}).filter(l -> {
|
||||
return toRequest(l.shortcut()).orElse(true);
|
||||
}).map(l -> {
|
||||
}).map(toFunction(l -> {
|
||||
return new DesktopIntegration(env, pkg, l);
|
||||
}).toList();
|
||||
})).toList();
|
||||
}
|
||||
}
|
||||
|
||||
static ShellCustomAction create(BuildEnv env, Package pkg) {
|
||||
static ShellCustomAction create(BuildEnv env, Package pkg) throws IOException {
|
||||
if (pkg.isRuntimeInstaller()) {
|
||||
return ShellCustomAction.nop(REPLACEMENT_STRING_IDS);
|
||||
}
|
||||
|
||||
@@ -25,19 +25,362 @@
|
||||
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import jdk.jpackage.internal.model.LinuxDebPackage;
|
||||
import jdk.jpackage.internal.model.LinuxPackage;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
import jdk.jpackage.internal.model.StandardPackageType;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.LinuxDebPackage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.internal.model.AppImageLayout;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
|
||||
import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB;
|
||||
|
||||
public class LinuxDebBundler extends LinuxPackageBundler {
|
||||
|
||||
private static final String TOOL_DPKG_DEB = "dpkg-deb";
|
||||
private static final String TOOL_DPKG = "dpkg";
|
||||
private static final String TOOL_FAKEROOT = "fakeroot";
|
||||
|
||||
public LinuxDebBundler() {
|
||||
super(LinuxFromParams.DEB_PACKAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(BuildEnv env, LinuxPackage pkg) throws ConfigException {
|
||||
|
||||
// Show warning if license file is missing
|
||||
if (pkg.licenseFile().isEmpty()) {
|
||||
Log.verbose(I18N.getString("message.debs-like-licenses"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ToolValidator> getToolValidators() {
|
||||
return Stream.of(TOOL_DPKG_DEB, TOOL_DPKG, TOOL_FAKEROOT).map(
|
||||
ToolValidator::new).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createConfigFiles(Map<String, String> replacementData,
|
||||
BuildEnv env, LinuxPackage pkg) throws IOException {
|
||||
prepareProjectConfig(replacementData, env, pkg);
|
||||
adjustPermissionsRecursive(env.appImageDir());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Path buildPackageBundle(BuildEnv env, LinuxPackage pkg,
|
||||
Path outputParentDir) throws PackagerException, IOException {
|
||||
return buildDeb(env, pkg, outputParentDir);
|
||||
}
|
||||
|
||||
private static final Pattern PACKAGE_NAME_REGEX = Pattern.compile("^(^\\S+):");
|
||||
|
||||
@Override
|
||||
protected void initLibProvidersLookup(LibProvidersLookup libProvidersLookup) {
|
||||
|
||||
libProvidersLookup.setPackageLookup(file -> {
|
||||
Path realPath = file.toRealPath();
|
||||
|
||||
try {
|
||||
// Try the real path first as it works better on newer Ubuntu versions
|
||||
return findProvidingPackages(realPath);
|
||||
} catch (IOException ex) {
|
||||
// Try the default path if differ
|
||||
if (!realPath.toString().equals(file.toString())) {
|
||||
return findProvidingPackages(file);
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static Stream<String> findProvidingPackages(Path file) throws IOException {
|
||||
//
|
||||
// `dpkg -S` command does glob pattern lookup. If not the absolute path
|
||||
// to the file is specified it might return mltiple package names.
|
||||
// Even for full paths multiple package names can be returned as
|
||||
// it is OK for multiple packages to provide the same file. `/opt`
|
||||
// directory is such an example. So we have to deal with multiple
|
||||
// packages per file situation.
|
||||
//
|
||||
// E.g.: `dpkg -S libc.so.6` command reports three packages:
|
||||
// libc6-x32: /libx32/libc.so.6
|
||||
// libc6:amd64: /lib/x86_64-linux-gnu/libc.so.6
|
||||
// libc6-i386: /lib32/libc.so.6
|
||||
// `:amd64` is architecture suffix and can (should) be dropped.
|
||||
// Still need to decide what package to choose from three.
|
||||
// libc6-x32 and libc6-i386 both depend on libc6:
|
||||
// $ dpkg -s libc6-x32
|
||||
// Package: libc6-x32
|
||||
// Status: install ok installed
|
||||
// Priority: optional
|
||||
// Section: libs
|
||||
// Installed-Size: 10840
|
||||
// Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
|
||||
// Architecture: amd64
|
||||
// Source: glibc
|
||||
// Version: 2.23-0ubuntu10
|
||||
// Depends: libc6 (= 2.23-0ubuntu10)
|
||||
//
|
||||
// We can dive into tracking dependencies, but this would be overly
|
||||
// complicated.
|
||||
//
|
||||
// For simplicity lets consider the following rules:
|
||||
// 1. If there is one item in `dpkg -S` output, accept it.
|
||||
// 2. If there are multiple items in `dpkg -S` output and there is at
|
||||
// least one item with the default arch suffix (DEB_ARCH),
|
||||
// accept only these items.
|
||||
// 3. If there are multiple items in `dpkg -S` output and there are
|
||||
// no with the default arch suffix (DEB_ARCH), accept all items.
|
||||
// So lets use this heuristics: don't accept packages for whom
|
||||
// `dpkg -p` command fails.
|
||||
// 4. Arch suffix should be stripped from accepted package names.
|
||||
//
|
||||
|
||||
Set<String> archPackages = new HashSet<>();
|
||||
Set<String> otherPackages = new HashSet<>();
|
||||
|
||||
var debArch = LinuxPackageArch.getValue(LINUX_DEB);
|
||||
|
||||
Executor.of(TOOL_DPKG, "-S", file.toString())
|
||||
.saveOutput(true).executeExpectSuccess()
|
||||
.getOutput().forEach(line -> {
|
||||
Matcher matcher = PACKAGE_NAME_REGEX.matcher(line);
|
||||
if (matcher.find()) {
|
||||
String name = matcher.group(1);
|
||||
if (name.endsWith(":" + debArch)) {
|
||||
// Strip arch suffix
|
||||
name = name.substring(0,
|
||||
name.length() - (debArch.length() + 1));
|
||||
archPackages.add(name);
|
||||
} else {
|
||||
otherPackages.add(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!archPackages.isEmpty()) {
|
||||
return archPackages.stream();
|
||||
}
|
||||
return otherPackages.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ConfigException> verifyOutputBundle(BuildEnv env, LinuxPackage pkg,
|
||||
Path packageBundle) {
|
||||
List<ConfigException> errors = new ArrayList<>();
|
||||
|
||||
String controlFileName = "control";
|
||||
|
||||
List<PackageProperty> properties = List.of(
|
||||
new PackageProperty("Package", pkg.packageName(),
|
||||
"APPLICATION_PACKAGE", controlFileName),
|
||||
new PackageProperty("Version", ((LinuxDebPackage)pkg).versionWithRelease(),
|
||||
"APPLICATION_VERSION_WITH_RELEASE",
|
||||
controlFileName),
|
||||
new PackageProperty("Architecture", pkg.arch(), "APPLICATION_ARCH", controlFileName));
|
||||
|
||||
List<String> cmdline = new ArrayList<>(List.of(TOOL_DPKG_DEB, "-f",
|
||||
packageBundle.toString()));
|
||||
properties.forEach(property -> cmdline.add(property.name));
|
||||
try {
|
||||
Map<String, String> actualValues = Executor.of(cmdline.toArray(String[]::new))
|
||||
.saveOutput(true)
|
||||
.executeExpectSuccess()
|
||||
.getOutput().stream()
|
||||
.map(line -> line.split(":\\s+", 2))
|
||||
.collect(Collectors.toMap(
|
||||
components -> components[0],
|
||||
components -> components[1]));
|
||||
properties.forEach(property -> errors.add(property.verifyValue(
|
||||
actualValues.get(property.name))));
|
||||
} catch (IOException ex) {
|
||||
// Ignore error as it is not critical. Just report it.
|
||||
Log.verbose(ex);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/*
|
||||
* set permissions with a string like "rwxr-xr-x"
|
||||
*
|
||||
* This cannot be directly backport to 22u which is built with 1.6
|
||||
*/
|
||||
private void setPermissions(Path file, String permissions) {
|
||||
Set<PosixFilePermission> filePermissions =
|
||||
PosixFilePermissions.fromString(permissions);
|
||||
try {
|
||||
if (Files.exists(file)) {
|
||||
Files.setPosixFilePermissions(file, filePermissions);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Log.error(ex.getMessage());
|
||||
Log.verbose(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean isDebian() {
|
||||
// we are just going to run "dpkg -s coreutils" and assume Debian
|
||||
// or deritive if no error is returned.
|
||||
try {
|
||||
Executor.of(TOOL_DPKG, "-s", "coreutils").executeExpectSuccess();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
// just fall thru
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void adjustPermissionsRecursive(Path dir) throws IOException {
|
||||
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file,
|
||||
BasicFileAttributes attrs)
|
||||
throws IOException {
|
||||
if (file.endsWith(".so") || !Files.isExecutable(file)) {
|
||||
setPermissions(file, "rw-r--r--");
|
||||
} else if (Files.isExecutable(file)) {
|
||||
setPermissions(file, "rwxr-xr-x");
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException e)
|
||||
throws IOException {
|
||||
if (e == null) {
|
||||
setPermissions(dir, "rwxr-xr-x");
|
||||
return FileVisitResult.CONTINUE;
|
||||
} else {
|
||||
// directory iteration failed
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class DebianFile {
|
||||
|
||||
DebianFile(Path dstFilePath, String comment) {
|
||||
this.dstFilePath = dstFilePath;
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
DebianFile setExecutable() {
|
||||
permissions = "rwxr-xr-x";
|
||||
return this;
|
||||
}
|
||||
|
||||
void create(Map<String, String> data, Function<String, OverridableResource> resourceFactory)
|
||||
throws IOException {
|
||||
resourceFactory.apply("template." + dstFilePath.getFileName().toString())
|
||||
.setCategory(I18N.getString(comment))
|
||||
.setSubstitutionData(data)
|
||||
.saveToFile(dstFilePath);
|
||||
if (permissions != null) {
|
||||
setPermissions(dstFilePath, permissions);
|
||||
}
|
||||
}
|
||||
|
||||
private final Path dstFilePath;
|
||||
private final String comment;
|
||||
private String permissions;
|
||||
}
|
||||
|
||||
private void prepareProjectConfig(Map<String, String> data, BuildEnv env, LinuxPackage pkg) throws IOException {
|
||||
|
||||
Path configDir = env.appImageDir().resolve("DEBIAN");
|
||||
List<DebianFile> debianFiles = new ArrayList<>();
|
||||
debianFiles.add(new DebianFile(
|
||||
configDir.resolve("control"),
|
||||
"resource.deb-control-file"));
|
||||
debianFiles.add(new DebianFile(
|
||||
configDir.resolve("preinst"),
|
||||
"resource.deb-preinstall-script").setExecutable());
|
||||
debianFiles.add(new DebianFile(
|
||||
configDir.resolve("prerm"),
|
||||
"resource.deb-prerm-script").setExecutable());
|
||||
debianFiles.add(new DebianFile(
|
||||
configDir.resolve("postinst"),
|
||||
"resource.deb-postinstall-script").setExecutable());
|
||||
debianFiles.add(new DebianFile(
|
||||
configDir.resolve("postrm"),
|
||||
"resource.deb-postrm-script").setExecutable());
|
||||
|
||||
((LinuxDebPackage)pkg).relativeCopyrightFilePath().ifPresent(copyrightFile -> {
|
||||
debianFiles.add(new DebianFile(env.appImageDir().resolve(copyrightFile),
|
||||
"resource.copyright-file"));
|
||||
});
|
||||
|
||||
for (DebianFile debianFile : debianFiles) {
|
||||
debianFile.create(data, env::createResource);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> createReplacementData(BuildEnv env, LinuxPackage pkg) throws IOException {
|
||||
Map<String, String> data = new HashMap<>();
|
||||
|
||||
String licenseText = pkg.licenseFile().map(toFunction(Files::readString)).orElse("Unknown");
|
||||
|
||||
data.put("APPLICATION_MAINTAINER", ((LinuxDebPackage) pkg).maintainer());
|
||||
data.put("APPLICATION_SECTION", pkg.category().orElseThrow());
|
||||
data.put("APPLICATION_COPYRIGHT", pkg.app().copyright());
|
||||
data.put("APPLICATION_LICENSE_TEXT", licenseText);
|
||||
data.put("APPLICATION_ARCH", pkg.arch());
|
||||
data.put("APPLICATION_INSTALLED_SIZE", Long.toString(
|
||||
AppImageLayout.toPathGroup(env.appImageLayout()).sizeInBytes() >> 10));
|
||||
data.put("APPLICATION_HOMEPAGE", pkg.aboutURL().map(
|
||||
value -> "Homepage: " + value).orElse(""));
|
||||
data.put("APPLICATION_VERSION_WITH_RELEASE", ((LinuxDebPackage) pkg).versionWithRelease());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private Path buildDeb(BuildEnv env, LinuxPackage pkg, Path outdir) throws IOException {
|
||||
Path outFile = outdir.resolve(pkg.packageFileNameWithSuffix());
|
||||
Log.verbose(I18N.format("message.outputting-to-location", outFile.toAbsolutePath()));
|
||||
|
||||
List<String> cmdline = new ArrayList<>();
|
||||
cmdline.addAll(List.of(TOOL_FAKEROOT, TOOL_DPKG_DEB));
|
||||
if (Log.isVerbose()) {
|
||||
cmdline.add("--verbose");
|
||||
}
|
||||
cmdline.addAll(List.of("-b", env.appImageDir().toString(),
|
||||
outFile.toAbsolutePath().toString()));
|
||||
|
||||
// run dpkg
|
||||
RetryExecutor.retryOnKnownErrorMessage(
|
||||
"semop(1): encountered an error: Invalid argument").execute(
|
||||
cmdline.toArray(String[]::new));
|
||||
|
||||
Log.verbose(I18N.format("message.output-to-location", outFile.toAbsolutePath()));
|
||||
|
||||
return outFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return I18N.getString("deb.bundler.name");
|
||||
@@ -49,28 +392,12 @@ public class LinuxDebBundler extends LinuxPackageBundler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path execute(Map<String, ? super Object> params, Path outputParentDir) throws PackagerException {
|
||||
|
||||
return Packager.<LinuxDebPackage>build().outputDir(outputParentDir)
|
||||
.pkg(LinuxFromParams.DEB_PACKAGE.fetchFrom(params))
|
||||
.env(BuildEnvFromParams.BUILD_ENV.fetchFrom(params))
|
||||
.pipelineBuilderMutatorFactory((env, pkg, outputDir) -> {
|
||||
return new LinuxDebPackager(env, pkg, outputDir, sysEnv.orElseThrow());
|
||||
}).execute(LinuxPackagingPipeline.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<LinuxDebSystemEnvironment> sysEnv() {
|
||||
return sysEnv;
|
||||
public boolean supported(boolean runtimeInstaller) {
|
||||
return OperatingSystem.isLinux() && (new ToolValidator(TOOL_DPKG_DEB).validate() == null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault() {
|
||||
return sysEnv.value()
|
||||
.map(LinuxSystemEnvironment::nativePackageType)
|
||||
.map(StandardPackageType.LINUX_DEB::equals)
|
||||
.orElse(false);
|
||||
return isDebian();
|
||||
}
|
||||
|
||||
private final Result<LinuxDebSystemEnvironment> sysEnv = LinuxDebSystemEnvironment.create(SYS_ENV);
|
||||
}
|
||||
|
||||
@@ -1,344 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
* 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;
|
||||
|
||||
import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PackageTaskID;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PrimaryTaskID;
|
||||
import jdk.jpackage.internal.model.AppImageLayout;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.LinuxDebPackage;
|
||||
|
||||
final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
|
||||
LinuxDebPackager(BuildEnv env, LinuxDebPackage pkg, Path outputDir, LinuxDebSystemEnvironment sysEnv) {
|
||||
super(env, pkg, outputDir, sysEnv);
|
||||
this.sysEnv = Objects.requireNonNull(sysEnv);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createConfigFiles(Map<String, String> replacementData) throws IOException {
|
||||
prepareProjectConfig(replacementData);
|
||||
adjustPermissionsRecursive();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initLibProvidersLookup(LibProvidersLookup libProvidersLookup) {
|
||||
|
||||
libProvidersLookup.setPackageLookup(file -> {
|
||||
Path realPath = file.toRealPath();
|
||||
|
||||
try {
|
||||
// Try the real path first as it works better on newer Ubuntu versions
|
||||
return findProvidingPackages(realPath, sysEnv.dpkg());
|
||||
} catch (IOException ex) {
|
||||
// Try the default path if differ
|
||||
if (!realPath.equals(file)) {
|
||||
return findProvidingPackages(file, sysEnv.dpkg());
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<? extends Exception> findErrorsInOutputPackage() throws IOException {
|
||||
List<ConfigException> errors = new ArrayList<>();
|
||||
|
||||
var controlFileName = "control";
|
||||
|
||||
List<PackageProperty> properties = List.of(
|
||||
new PackageProperty("Package", pkg.packageName(),
|
||||
"APPLICATION_PACKAGE", controlFileName),
|
||||
new PackageProperty("Version", pkg.versionWithRelease(),
|
||||
"APPLICATION_VERSION_WITH_RELEASE",
|
||||
controlFileName),
|
||||
new PackageProperty("Architecture", pkg.arch(), "APPLICATION_ARCH", controlFileName));
|
||||
|
||||
List<String> cmdline = new ArrayList<>(List.of(
|
||||
sysEnv.dpkgdeb().toString(), "-f", outputPackageFile().toString()));
|
||||
|
||||
properties.forEach(property -> cmdline.add(property.name));
|
||||
|
||||
Map<String, String> actualValues = Executor.of(cmdline.toArray(String[]::new))
|
||||
.saveOutput(true)
|
||||
.executeExpectSuccess()
|
||||
.getOutput().stream()
|
||||
.map(line -> line.split(":\\s+", 2))
|
||||
.collect(Collectors.toMap(
|
||||
components -> components[0],
|
||||
components -> components[1]));
|
||||
|
||||
for (var property : properties) {
|
||||
Optional.ofNullable(property.verifyValue(actualValues.get(property.name))).ifPresent(errors::add);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> createReplacementData() throws IOException {
|
||||
Map<String, String> data = new HashMap<>();
|
||||
|
||||
String licenseText = pkg.licenseFile().map(toFunction(Files::readString)).orElse("Unknown");
|
||||
|
||||
data.put("APPLICATION_MAINTAINER", pkg.maintainer());
|
||||
data.put("APPLICATION_SECTION", pkg.category().orElseThrow());
|
||||
data.put("APPLICATION_COPYRIGHT", pkg.app().copyright());
|
||||
data.put("APPLICATION_LICENSE_TEXT", licenseText);
|
||||
data.put("APPLICATION_ARCH", pkg.arch());
|
||||
data.put("APPLICATION_INSTALLED_SIZE", Long.toString(
|
||||
AppImageLayout.toPathGroup(env.appImageLayout()).sizeInBytes() >> 10));
|
||||
data.put("APPLICATION_HOMEPAGE", pkg.aboutURL().map(
|
||||
value -> "Homepage: " + value).orElse(""));
|
||||
data.put("APPLICATION_VERSION_WITH_RELEASE", pkg.versionWithRelease());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildPackage() throws IOException {
|
||||
|
||||
Path debFile = outputPackageFile();
|
||||
|
||||
Log.verbose(I18N.format("message.outputting-to-location", debFile.toAbsolutePath()));
|
||||
|
||||
List<String> cmdline = new ArrayList<>();
|
||||
Stream.of(sysEnv.fakeroot(), sysEnv.dpkgdeb()).map(Path::toString).forEach(cmdline::add);
|
||||
if (Log.isVerbose()) {
|
||||
cmdline.add("--verbose");
|
||||
}
|
||||
cmdline.addAll(List.of("-b", env.appImageDir().toString(), debFile.toAbsolutePath().toString()));
|
||||
|
||||
// run dpkg
|
||||
RetryExecutor.retryOnKnownErrorMessage(
|
||||
"semop(1): encountered an error: Invalid argument").execute(
|
||||
cmdline.toArray(String[]::new));
|
||||
|
||||
Log.verbose(I18N.format("message.output-to-location", debFile.toAbsolutePath()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(PackagingPipeline.Builder pipelineBuilder) {
|
||||
super.accept(pipelineBuilder);
|
||||
|
||||
// Build deb config files after app image contents are ready because
|
||||
// it calculates the size of the image and saves the value in one of the config files.
|
||||
pipelineBuilder.configuredTasks().filter(task -> {
|
||||
return PackageTaskID.CREATE_CONFIG_FILES.equals(task.task());
|
||||
}).findFirst().orElseThrow()
|
||||
.addDependencies(PrimaryTaskID.BUILD_APPLICATION_IMAGE, PrimaryTaskID.COPY_APP_IMAGE)
|
||||
.add();
|
||||
}
|
||||
|
||||
private void adjustPermissionsRecursive() throws IOException {
|
||||
Files.walkFileTree(env.appImageDir(), new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (file.endsWith(".so") || !Files.isExecutable(file)) {
|
||||
Files.setPosixFilePermissions(file, SO_PERMISSIONS);
|
||||
} else if (Files.isExecutable(file)) {
|
||||
Files.setPosixFilePermissions(file, EXECUTABLE_PERMISSIONS);
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
|
||||
if (e == null) {
|
||||
Files.setPosixFilePermissions(dir, FOLDER_PERMISSIONS);
|
||||
return FileVisitResult.CONTINUE;
|
||||
} else {
|
||||
// directory iteration failed
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void prepareProjectConfig(Map<String, String> data) throws IOException {
|
||||
|
||||
Path configDir = env.appImageDir().resolve("DEBIAN");
|
||||
List<DebianFile> debianFiles = new ArrayList<>();
|
||||
debianFiles.add(new DebianFile(
|
||||
configDir.resolve("control"),
|
||||
"resource.deb-control-file"));
|
||||
debianFiles.add(new DebianFile(
|
||||
configDir.resolve("preinst"),
|
||||
"resource.deb-preinstall-script").setExecutable());
|
||||
debianFiles.add(new DebianFile(
|
||||
configDir.resolve("prerm"),
|
||||
"resource.deb-prerm-script").setExecutable());
|
||||
debianFiles.add(new DebianFile(
|
||||
configDir.resolve("postinst"),
|
||||
"resource.deb-postinstall-script").setExecutable());
|
||||
debianFiles.add(new DebianFile(
|
||||
configDir.resolve("postrm"),
|
||||
"resource.deb-postrm-script").setExecutable());
|
||||
|
||||
pkg.relativeCopyrightFilePath().ifPresent(copyrightFile -> {
|
||||
debianFiles.add(new DebianFile(env.appImageDir().resolve(copyrightFile),
|
||||
"resource.copyright-file"));
|
||||
});
|
||||
|
||||
for (DebianFile debianFile : debianFiles) {
|
||||
debianFile.create(data, env::createResource);
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream<String> findProvidingPackages(Path file, Path dpkg) throws IOException {
|
||||
//
|
||||
// `dpkg -S` command does glob pattern lookup. If not the absolute path
|
||||
// to the file is specified it might return mltiple package names.
|
||||
// Even for full paths multiple package names can be returned as
|
||||
// it is OK for multiple packages to provide the same file. `/opt`
|
||||
// directory is such an example. So we have to deal with multiple
|
||||
// packages per file situation.
|
||||
//
|
||||
// E.g.: `dpkg -S libc.so.6` command reports three packages:
|
||||
// libc6-x32: /libx32/libc.so.6
|
||||
// libc6:amd64: /lib/x86_64-linux-gnu/libc.so.6
|
||||
// libc6-i386: /lib32/libc.so.6
|
||||
// `:amd64` is architecture suffix and can (should) be dropped.
|
||||
// Still need to decide what package to choose from three.
|
||||
// libc6-x32 and libc6-i386 both depend on libc6:
|
||||
// $ dpkg -s libc6-x32
|
||||
// Package: libc6-x32
|
||||
// Status: install ok installed
|
||||
// Priority: optional
|
||||
// Section: libs
|
||||
// Installed-Size: 10840
|
||||
// Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
|
||||
// Architecture: amd64
|
||||
// Source: glibc
|
||||
// Version: 2.23-0ubuntu10
|
||||
// Depends: libc6 (= 2.23-0ubuntu10)
|
||||
//
|
||||
// We can dive into tracking dependencies, but this would be overly
|
||||
// complicated.
|
||||
//
|
||||
// For simplicity lets consider the following rules:
|
||||
// 1. If there is one item in `dpkg -S` output, accept it.
|
||||
// 2. If there are multiple items in `dpkg -S` output and there is at
|
||||
// least one item with the default arch suffix (DEB_ARCH),
|
||||
// accept only these items.
|
||||
// 3. If there are multiple items in `dpkg -S` output and there are
|
||||
// no with the default arch suffix (DEB_ARCH), accept all items.
|
||||
// So lets use this heuristics: don't accept packages for whom
|
||||
// `dpkg -p` command fails.
|
||||
// 4. Arch suffix should be stripped from accepted package names.
|
||||
//
|
||||
|
||||
Set<String> archPackages = new HashSet<>();
|
||||
Set<String> otherPackages = new HashSet<>();
|
||||
|
||||
var debArch = LinuxPackageArch.getValue(LINUX_DEB);
|
||||
|
||||
Executor.of(dpkg.toString(), "-S", file.toString())
|
||||
.saveOutput(true).executeExpectSuccess()
|
||||
.getOutput().forEach(line -> {
|
||||
Matcher matcher = PACKAGE_NAME_REGEX.matcher(line);
|
||||
if (matcher.find()) {
|
||||
String name = matcher.group(1);
|
||||
if (name.endsWith(":" + debArch)) {
|
||||
// Strip arch suffix
|
||||
name = name.substring(0,
|
||||
name.length() - (debArch.length() + 1));
|
||||
archPackages.add(name);
|
||||
} else {
|
||||
otherPackages.add(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!archPackages.isEmpty()) {
|
||||
return archPackages.stream();
|
||||
}
|
||||
return otherPackages.stream();
|
||||
}
|
||||
|
||||
|
||||
private static final class DebianFile {
|
||||
|
||||
DebianFile(Path dstFilePath, String comment) {
|
||||
this.dstFilePath = Objects.requireNonNull(dstFilePath);
|
||||
this.comment = Objects.requireNonNull(comment);
|
||||
}
|
||||
|
||||
DebianFile setExecutable() {
|
||||
permissions = EXECUTABLE_PERMISSIONS;
|
||||
return this;
|
||||
}
|
||||
|
||||
void create(Map<String, String> data, Function<String, OverridableResource> resourceFactory)
|
||||
throws IOException {
|
||||
resourceFactory.apply("template." + dstFilePath.getFileName().toString())
|
||||
.setCategory(I18N.getString(comment))
|
||||
.setSubstitutionData(data)
|
||||
.saveToFile(dstFilePath);
|
||||
if (permissions != null) {
|
||||
Files.setPosixFilePermissions(dstFilePath, permissions);
|
||||
}
|
||||
}
|
||||
|
||||
private final Path dstFilePath;
|
||||
private final String comment;
|
||||
private Set<PosixFilePermission> permissions;
|
||||
}
|
||||
|
||||
|
||||
private final LinuxDebSystemEnvironment sysEnv;
|
||||
|
||||
private static final Pattern PACKAGE_NAME_REGEX = Pattern.compile("^(^\\S+):");
|
||||
|
||||
private static final Set<PosixFilePermission> EXECUTABLE_PERMISSIONS = PosixFilePermissions.fromString("rwxr-xr-x");
|
||||
private static final Set<PosixFilePermission> FOLDER_PERMISSIONS = PosixFilePermissions.fromString("rwxr-xr-x");
|
||||
private static final Set<PosixFilePermission> SO_PERMISSIONS = PosixFilePermissions.fromString("rw-r--r--");
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin;
|
||||
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxDebSystemEnvironment extends LinuxSystemEnvironment, LinuxDebSystemEnvironmentMixin {
|
||||
|
||||
static Result<LinuxDebSystemEnvironment> create(Result<LinuxSystemEnvironment> base) {
|
||||
return mixin(LinuxDebSystemEnvironment.class, base, LinuxDebSystemEnvironmentMixin::create);
|
||||
}
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxDebSystemEnvironmentMixin {
|
||||
Path dpkg();
|
||||
Path dpkgdeb();
|
||||
Path fakeroot();
|
||||
|
||||
record Stub(Path dpkg, Path dpkgdeb, Path fakeroot) implements LinuxDebSystemEnvironmentMixin {
|
||||
}
|
||||
|
||||
static Result<LinuxDebSystemEnvironmentMixin> create() {
|
||||
final var errors = Stream.of(Internal.TOOL_DPKG_DEB, Internal.TOOL_DPKG, Internal.TOOL_FAKEROOT)
|
||||
.map(ToolValidator::new)
|
||||
.map(ToolValidator::validate)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
if (errors.isEmpty()) {
|
||||
return Result.ofValue(new Stub(Internal.TOOL_DPKG, Internal.TOOL_DPKG_DEB, Internal.TOOL_FAKEROOT));
|
||||
} else {
|
||||
return Result.ofErrors(errors);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Internal {
|
||||
|
||||
private static final Path TOOL_DPKG_DEB = Path.of("dpkg-deb");
|
||||
private static final Path TOOL_DPKG = Path.of("dpkg");
|
||||
private static final Path TOOL_FAKEROOT = Path.of("fakeroot");
|
||||
}
|
||||
}
|
||||
@@ -39,10 +39,9 @@ import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.LinuxApplication;
|
||||
import jdk.jpackage.internal.model.LinuxDebPackage;
|
||||
import jdk.jpackage.internal.model.LinuxLauncher;
|
||||
import jdk.jpackage.internal.model.LinuxLauncherMixin;
|
||||
import jdk.jpackage.internal.model.LinuxRpmPackage;
|
||||
import jdk.jpackage.internal.model.LinuxPackage;
|
||||
import jdk.jpackage.internal.model.StandardPackageType;
|
||||
|
||||
final class LinuxFromParams {
|
||||
@@ -77,7 +76,7 @@ final class LinuxFromParams {
|
||||
return pkgBuilder;
|
||||
}
|
||||
|
||||
private static LinuxRpmPackage createLinuxRpmPackage(
|
||||
private static LinuxPackage createLinuxRpmPackage(
|
||||
Map<String, ? super Object> params) throws ConfigException, IOException {
|
||||
|
||||
final var superPkgBuilder = createLinuxPackageBuilder(params, LINUX_RPM);
|
||||
@@ -89,7 +88,7 @@ final class LinuxFromParams {
|
||||
return pkgBuilder.create();
|
||||
}
|
||||
|
||||
private static LinuxDebPackage createLinuxDebPackage(
|
||||
private static LinuxPackage createLinuxDebPackage(
|
||||
Map<String, ? super Object> params) throws ConfigException, IOException {
|
||||
|
||||
final var superPkgBuilder = createLinuxPackageBuilder(params, LINUX_DEB);
|
||||
@@ -98,23 +97,16 @@ final class LinuxFromParams {
|
||||
|
||||
MAINTAINER_EMAIL.copyInto(params, pkgBuilder::maintainerEmail);
|
||||
|
||||
final var pkg = pkgBuilder.create();
|
||||
|
||||
// Show warning if license file is missing
|
||||
if (pkg.licenseFile().isEmpty()) {
|
||||
Log.verbose(I18N.getString("message.debs-like-licenses"));
|
||||
}
|
||||
|
||||
return pkg;
|
||||
return pkgBuilder.create();
|
||||
}
|
||||
|
||||
static final BundlerParamInfo<LinuxApplication> APPLICATION = createApplicationBundlerParam(
|
||||
LinuxFromParams::createLinuxApplication);
|
||||
|
||||
static final BundlerParamInfo<LinuxRpmPackage> RPM_PACKAGE = createPackageBundlerParam(
|
||||
static final BundlerParamInfo<LinuxPackage> RPM_PACKAGE = createPackageBundlerParam(
|
||||
LinuxFromParams::createLinuxRpmPackage);
|
||||
|
||||
static final BundlerParamInfo<LinuxDebPackage> DEB_PACKAGE = createPackageBundlerParam(
|
||||
static final BundlerParamInfo<LinuxPackage> DEB_PACKAGE = createPackageBundlerParam(
|
||||
LinuxFromParams::createLinuxDebPackage);
|
||||
|
||||
private static final BundlerParamInfo<String> LINUX_SHORTCUT_HINT = createStringBundlerParam(
|
||||
|
||||
@@ -38,7 +38,7 @@ import jdk.jpackage.internal.model.Package;
|
||||
*/
|
||||
public final class LinuxLaunchersAsServices extends UnixLaunchersAsServices {
|
||||
|
||||
private LinuxLaunchersAsServices(BuildEnv env, Package pkg) {
|
||||
private LinuxLaunchersAsServices(BuildEnv env, Package pkg) throws IOException {
|
||||
super(env.appImageDir(), pkg.app(), REQUIRED_PACKAGES, launcher -> {
|
||||
return new LauncherImpl(env, pkg, launcher);
|
||||
});
|
||||
@@ -58,7 +58,7 @@ public final class LinuxLaunchersAsServices extends UnixLaunchersAsServices {
|
||||
return data;
|
||||
}
|
||||
|
||||
static ShellCustomAction create(BuildEnv env, Package pkg) {
|
||||
static ShellCustomAction create(BuildEnv env, Package pkg) throws IOException {
|
||||
if (pkg.isRuntimeInstaller()) {
|
||||
return ShellCustomAction.nop(LINUX_REPLACEMENT_STRING_IDS);
|
||||
}
|
||||
|
||||
@@ -24,16 +24,33 @@
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PackageBuildEnv;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PackageTaskID;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PrimaryTaskID;
|
||||
import jdk.jpackage.internal.model.AppImageLayout;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.LinuxDebPackage;
|
||||
import jdk.jpackage.internal.model.LinuxPackage;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
|
||||
abstract class LinuxPackageBundler extends AbstractBundler {
|
||||
|
||||
LinuxPackageBundler(BundlerParamInfo<? extends LinuxPackage> pkgParam) {
|
||||
this.pkgParam = Objects.requireNonNull(pkgParam);
|
||||
this.pkgParam = pkgParam;
|
||||
customActions = List.of(new CustomActionInstance(
|
||||
DesktopIntegration::create), new CustomActionInstance(
|
||||
LinuxLaunchersAsServices::create));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -41,32 +58,39 @@ abstract class LinuxPackageBundler extends AbstractBundler {
|
||||
throws ConfigException {
|
||||
|
||||
// Order is important!
|
||||
pkgParam.fetchFrom(params);
|
||||
BuildEnvFromParams.BUILD_ENV.fetchFrom(params);
|
||||
LinuxPackage pkg = pkgParam.fetchFrom(params);
|
||||
var env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params);
|
||||
|
||||
LinuxSystemEnvironment sysEnv;
|
||||
try {
|
||||
sysEnv = sysEnv().orElseThrow();
|
||||
} catch (RuntimeException ex) {
|
||||
throw ConfigException.rethrowConfigException(ex);
|
||||
for (var validator: getToolValidators()) {
|
||||
ConfigException ex = validator.validate();
|
||||
if (ex != null) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDefault()) {
|
||||
Log.verbose(I18N.format(
|
||||
"message.not-default-bundler-no-dependencies-lookup",
|
||||
withFindNeededPackages = false;
|
||||
Log.verbose(MessageFormat.format(I18N.getString(
|
||||
"message.not-default-bundler-no-dependencies-lookup"),
|
||||
getName()));
|
||||
} else if (!sysEnv.soLookupAvailable()) {
|
||||
final String advice;
|
||||
if ("deb".equals(getID())) {
|
||||
advice = "message.deb-ldd-not-available.advice";
|
||||
} else {
|
||||
advice = "message.rpm-ldd-not-available.advice";
|
||||
} else {
|
||||
withFindNeededPackages = LibProvidersLookup.supported();
|
||||
if (!withFindNeededPackages) {
|
||||
final String advice;
|
||||
if ("deb".equals(getID())) {
|
||||
advice = "message.deb-ldd-not-available.advice";
|
||||
} else {
|
||||
advice = "message.rpm-ldd-not-available.advice";
|
||||
}
|
||||
// Let user know package dependencies will not be generated.
|
||||
Log.error(String.format("%s\n%s", I18N.getString(
|
||||
"message.ldd-not-available"), I18N.getString(advice)));
|
||||
}
|
||||
// Let user know package dependencies will not be generated.
|
||||
Log.error(String.format("%s\n%s", I18N.getString(
|
||||
"message.ldd-not-available"), I18N.getString(advice)));
|
||||
}
|
||||
|
||||
// Packaging specific validation
|
||||
doValidate(env, pkg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -76,13 +100,148 @@ abstract class LinuxPackageBundler extends AbstractBundler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supported(boolean runtimeInstaller) {
|
||||
return sysEnv().hasValue();
|
||||
public final Path execute(Map<String, ? super Object> params,
|
||||
Path outputParentDir) throws PackagerException {
|
||||
IOUtils.writableOutputDir(outputParentDir);
|
||||
|
||||
// Order is important!
|
||||
final LinuxPackage pkg = pkgParam.fetchFrom(params);
|
||||
final var env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params);
|
||||
|
||||
final var pipelineBuilder = LinuxPackagingPipeline.build()
|
||||
.excludeDirFromCopying(outputParentDir)
|
||||
.task(PackageTaskID.CREATE_PACKAGE_FILE)
|
||||
.packageAction(this::buildPackage)
|
||||
.add();
|
||||
|
||||
final var createConfigFilesTaskBuilder = pipelineBuilder
|
||||
.task(PackageTaskID.CREATE_CONFIG_FILES)
|
||||
.packageAction(this::buildConfigFiles);
|
||||
|
||||
if (pkg instanceof LinuxDebPackage) {
|
||||
// Build deb config files after app image contents are ready because
|
||||
// it calculates the size of the image and saves the value in one of the config files.
|
||||
createConfigFilesTaskBuilder.addDependencies(PrimaryTaskID.BUILD_APPLICATION_IMAGE, PrimaryTaskID.COPY_APP_IMAGE);
|
||||
}
|
||||
|
||||
createConfigFilesTaskBuilder.add();
|
||||
|
||||
pipelineBuilder.create().execute(env, pkg, outputParentDir);
|
||||
|
||||
return outputParentDir.resolve(pkg.packageFileNameWithSuffix()).toAbsolutePath();
|
||||
}
|
||||
|
||||
protected abstract Result<? extends LinuxSystemEnvironment> sysEnv();
|
||||
private void buildConfigFiles(PackageBuildEnv<LinuxPackage, AppImageLayout> env) throws PackagerException, IOException {
|
||||
for (var ca : customActions) {
|
||||
ca.init(env.env(), env.pkg());
|
||||
}
|
||||
|
||||
Map<String, String> data = createDefaultReplacementData(env.env(), env.pkg());
|
||||
|
||||
for (var ca : customActions) {
|
||||
ShellCustomAction.mergeReplacementData(data, ca.instance.create());
|
||||
}
|
||||
|
||||
data.putAll(createReplacementData(env.env(), env.pkg()));
|
||||
|
||||
createConfigFiles(Collections.unmodifiableMap(data), env.env(), env.pkg());
|
||||
}
|
||||
|
||||
private void buildPackage(PackageBuildEnv<LinuxPackage, AppImageLayout> env) throws PackagerException, IOException {
|
||||
Path packageBundle = buildPackageBundle(env.env(), env.pkg(), env.outputDir());
|
||||
|
||||
verifyOutputBundle(env.env(), env.pkg(), packageBundle).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.forEachOrdered(ex -> {
|
||||
Log.verbose(ex.getLocalizedMessage());
|
||||
Log.verbose(ex.getAdvice());
|
||||
});
|
||||
}
|
||||
|
||||
private List<String> getListOfNeededPackages(BuildEnv env) throws IOException {
|
||||
|
||||
final List<String> caPackages = customActions.stream()
|
||||
.map(ca -> ca.instance)
|
||||
.map(ShellCustomAction::requiredPackages)
|
||||
.flatMap(List::stream).toList();
|
||||
|
||||
final List<String> neededLibPackages;
|
||||
if (withFindNeededPackages) {
|
||||
LibProvidersLookup lookup = new LibProvidersLookup();
|
||||
initLibProvidersLookup(lookup);
|
||||
|
||||
neededLibPackages = lookup.execute(env.appImageDir());
|
||||
} else {
|
||||
neededLibPackages = Collections.emptyList();
|
||||
Log.info(I18N.getString("warning.foreign-app-image"));
|
||||
}
|
||||
|
||||
// Merge all package lists together.
|
||||
// Filter out empty names, sort and remove duplicates.
|
||||
List<String> result = Stream.of(caPackages, neededLibPackages).flatMap(
|
||||
List::stream).filter(Predicate.not(String::isEmpty)).sorted().distinct().toList();
|
||||
|
||||
Log.verbose(String.format("Required packages: %s", result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, String> createDefaultReplacementData(BuildEnv env, LinuxPackage pkg) throws IOException {
|
||||
Map<String, String> data = new HashMap<>();
|
||||
|
||||
data.put("APPLICATION_PACKAGE", pkg.packageName());
|
||||
data.put("APPLICATION_VENDOR", pkg.app().vendor());
|
||||
data.put("APPLICATION_VERSION", pkg.version());
|
||||
data.put("APPLICATION_DESCRIPTION", pkg.description());
|
||||
|
||||
String defaultDeps = String.join(", ", getListOfNeededPackages(env));
|
||||
String customDeps = pkg.additionalDependencies().orElse("");
|
||||
if (!customDeps.isEmpty() && !defaultDeps.isEmpty()) {
|
||||
customDeps = ", " + customDeps;
|
||||
}
|
||||
data.put("PACKAGE_DEFAULT_DEPENDENCIES", defaultDeps);
|
||||
data.put("PACKAGE_CUSTOM_DEPENDENCIES", customDeps);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
protected abstract List<ConfigException> verifyOutputBundle(
|
||||
BuildEnv env, LinuxPackage pkg, Path packageBundle);
|
||||
|
||||
protected abstract void initLibProvidersLookup(LibProvidersLookup libProvidersLookup);
|
||||
|
||||
protected abstract List<ToolValidator> getToolValidators();
|
||||
|
||||
protected abstract void doValidate(BuildEnv env, LinuxPackage pkg)
|
||||
throws ConfigException;
|
||||
|
||||
protected abstract Map<String, String> createReplacementData(
|
||||
BuildEnv env, LinuxPackage pkg) throws IOException;
|
||||
|
||||
protected abstract void createConfigFiles(
|
||||
Map<String, String> replacementData,
|
||||
BuildEnv env, LinuxPackage pkg) throws IOException;
|
||||
|
||||
protected abstract Path buildPackageBundle(
|
||||
BuildEnv env, LinuxPackage pkg, Path outputParentDir) throws
|
||||
PackagerException, IOException;
|
||||
|
||||
private final BundlerParamInfo<? extends LinuxPackage> pkgParam;
|
||||
private boolean withFindNeededPackages;
|
||||
private final List<CustomActionInstance> customActions;
|
||||
|
||||
static final Result<LinuxSystemEnvironment> SYS_ENV = LinuxSystemEnvironment.create();
|
||||
private static final class CustomActionInstance {
|
||||
|
||||
CustomActionInstance(ShellCustomActionFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
void init(BuildEnv env, Package pkg) throws IOException {
|
||||
instance = factory.create(env, pkg);
|
||||
Objects.requireNonNull(instance);
|
||||
}
|
||||
|
||||
private final ShellCustomActionFactory factory;
|
||||
ShellCustomAction instance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PackageTaskID;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PrimaryTaskID;
|
||||
import jdk.jpackage.internal.PackagingPipeline.TaskID;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.LinuxPackage;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
|
||||
abstract class LinuxPackager<T extends LinuxPackage> implements Consumer<PackagingPipeline.Builder> {
|
||||
|
||||
LinuxPackager(BuildEnv env, T pkg, Path outputDir, LinuxSystemEnvironment sysEnv) {
|
||||
this.env = Objects.requireNonNull(env);
|
||||
this.pkg = Objects.requireNonNull(pkg);
|
||||
this.outputDir = Objects.requireNonNull(outputDir);
|
||||
this.withRequiredPackagesLookup = sysEnv.soLookupAvailable() && sysEnv.nativePackageType().equals(pkg.type());
|
||||
|
||||
customActions = List.of(
|
||||
DesktopIntegration.create(env, pkg),
|
||||
LinuxLaunchersAsServices.create(env, pkg));
|
||||
}
|
||||
|
||||
enum LinuxPackageTaskID implements TaskID {
|
||||
INIT_REQUIRED_PACKAGES,
|
||||
VERIFY_PACKAGE
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(PackagingPipeline.Builder pipelineBuilder) {
|
||||
pipelineBuilder.excludeDirFromCopying(outputDir)
|
||||
.task(PackageTaskID.CREATE_CONFIG_FILES)
|
||||
.action(this::buildConfigFiles)
|
||||
.add()
|
||||
.task(LinuxPackageTaskID.INIT_REQUIRED_PACKAGES)
|
||||
.addDependencies(PrimaryTaskID.BUILD_APPLICATION_IMAGE, PrimaryTaskID.COPY_APP_IMAGE)
|
||||
.addDependent(PackageTaskID.CREATE_CONFIG_FILES)
|
||||
.action(this::initRequiredPackages)
|
||||
.add()
|
||||
.task(LinuxPackageTaskID.VERIFY_PACKAGE)
|
||||
.addDependencies(PackageTaskID.CREATE_PACKAGE_FILE)
|
||||
.addDependent(PrimaryTaskID.PACKAGE)
|
||||
.action(this::verifyOutputPackage)
|
||||
.add()
|
||||
.task(PackageTaskID.CREATE_PACKAGE_FILE)
|
||||
.action(this::buildPackage)
|
||||
.add();
|
||||
}
|
||||
|
||||
protected final Path outputPackageFile() {
|
||||
return outputDir.resolve(pkg.packageFileNameWithSuffix());
|
||||
}
|
||||
|
||||
protected abstract void buildPackage() throws IOException;
|
||||
|
||||
protected abstract List<? extends Exception> findErrorsInOutputPackage() throws IOException;
|
||||
|
||||
protected abstract void createConfigFiles(Map<String, String> replacementData) throws IOException;
|
||||
|
||||
protected abstract Map<String, String> createReplacementData() throws IOException;
|
||||
|
||||
protected abstract void initLibProvidersLookup(LibProvidersLookup libProvidersLookup);
|
||||
|
||||
private void buildConfigFiles() throws PackagerException, IOException {
|
||||
|
||||
final var data = createDefaultReplacementData();
|
||||
|
||||
for (var ca : customActions) {
|
||||
ShellCustomAction.mergeReplacementData(data, ca.create());
|
||||
}
|
||||
|
||||
data.putAll(createReplacementData());
|
||||
|
||||
createConfigFiles(Collections.unmodifiableMap(data));
|
||||
}
|
||||
|
||||
private Map<String, String> createDefaultReplacementData() {
|
||||
Map<String, String> data = new HashMap<>();
|
||||
|
||||
data.put("APPLICATION_PACKAGE", pkg.packageName());
|
||||
data.put("APPLICATION_VENDOR", pkg.app().vendor());
|
||||
data.put("APPLICATION_VERSION", pkg.version());
|
||||
data.put("APPLICATION_DESCRIPTION", pkg.description());
|
||||
|
||||
String defaultDeps = String.join(", ", requiredPackages);
|
||||
String customDeps = pkg.additionalDependencies().orElse("");
|
||||
if (!customDeps.isEmpty() && !defaultDeps.isEmpty()) {
|
||||
customDeps = ", " + customDeps;
|
||||
}
|
||||
data.put("PACKAGE_DEFAULT_DEPENDENCIES", defaultDeps);
|
||||
data.put("PACKAGE_CUSTOM_DEPENDENCIES", customDeps);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private void initRequiredPackages() throws IOException {
|
||||
|
||||
final List<String> caPackages = customActions.stream()
|
||||
.map(ShellCustomAction::requiredPackages)
|
||||
.flatMap(List::stream).toList();
|
||||
|
||||
final List<String> neededLibPackages;
|
||||
if (withRequiredPackagesLookup) {
|
||||
neededLibPackages = findRequiredPackages();
|
||||
} else {
|
||||
neededLibPackages = Collections.emptyList();
|
||||
Log.info(I18N.getString("warning.foreign-app-image"));
|
||||
}
|
||||
|
||||
// Merge all package lists together.
|
||||
// Filter out empty names, sort and remove duplicates.
|
||||
Stream.of(caPackages, neededLibPackages)
|
||||
.flatMap(List::stream)
|
||||
.filter(Predicate.not(String::isEmpty))
|
||||
.sorted().distinct().forEach(requiredPackages::add);
|
||||
|
||||
Log.verbose(String.format("Required packages: %s", requiredPackages));
|
||||
}
|
||||
|
||||
private List<String> findRequiredPackages() throws IOException {
|
||||
var lookup = new LibProvidersLookup();
|
||||
initLibProvidersLookup(lookup);
|
||||
return lookup.execute(env.appImageDir());
|
||||
}
|
||||
|
||||
private void verifyOutputPackage() {
|
||||
final List<? extends Exception> errors;
|
||||
try {
|
||||
errors = findErrorsInOutputPackage();
|
||||
} catch (Exception ex) {
|
||||
// Ignore error as it is not critical. Just report it.
|
||||
Log.verbose(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
for (var ex : errors) {
|
||||
Log.verbose(ex.getLocalizedMessage());
|
||||
if (ex instanceof ConfigException cfgEx) {
|
||||
Log.verbose(cfgEx.getAdvice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final BuildEnv env;
|
||||
protected final T pkg;
|
||||
protected final Path outputDir;
|
||||
private final boolean withRequiredPackagesLookup;
|
||||
private final List<String> requiredPackages = new ArrayList<>();
|
||||
private final List<ShellCustomAction> customActions;
|
||||
}
|
||||
@@ -25,20 +25,189 @@
|
||||
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.DottedVersion;
|
||||
import jdk.jpackage.internal.model.LinuxPackage;
|
||||
import jdk.jpackage.internal.model.LinuxRpmPackage;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
import jdk.jpackage.internal.model.StandardPackageType;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
|
||||
/**
|
||||
* There are two command line options to configure license information for RPM
|
||||
* packaging: --linux-rpm-license-type and --license-file. Value of
|
||||
* --linux-rpm-license-type command line option configures "License:" section
|
||||
* of RPM spec. Value of --license-file command line option specifies a license
|
||||
* file to be added to the package. License file is a sort of documentation file
|
||||
* but it will be installed even if user selects an option to install the
|
||||
* package without documentation. --linux-rpm-license-type is the primary option
|
||||
* to set license information. --license-file makes little sense in case of RPM
|
||||
* packaging.
|
||||
*/
|
||||
public class LinuxRpmBundler extends LinuxPackageBundler {
|
||||
|
||||
private static final String DEFAULT_SPEC_TEMPLATE = "template.spec";
|
||||
|
||||
public static final String TOOL_RPM = "rpm";
|
||||
public static final String TOOL_RPMBUILD = "rpmbuild";
|
||||
public static final DottedVersion TOOL_RPMBUILD_MIN_VERSION = DottedVersion.lazy(
|
||||
"4.10");
|
||||
|
||||
public LinuxRpmBundler() {
|
||||
super(LinuxFromParams.RPM_PACKAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(BuildEnv env, LinuxPackage pkg) throws ConfigException {
|
||||
}
|
||||
|
||||
private static ToolValidator createRpmbuildToolValidator() {
|
||||
Pattern pattern = Pattern.compile(" (\\d+\\.\\d+)");
|
||||
return new ToolValidator(TOOL_RPMBUILD).setMinimalVersion(
|
||||
TOOL_RPMBUILD_MIN_VERSION).setVersionParser(lines -> {
|
||||
String versionString = lines.limit(1).collect(
|
||||
Collectors.toList()).get(0);
|
||||
Matcher matcher = pattern.matcher(versionString);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ToolValidator> getToolValidators() {
|
||||
return List.of(createRpmbuildToolValidator());
|
||||
}
|
||||
|
||||
protected void createConfigFiles(Map<String, String> replacementData,
|
||||
BuildEnv env, LinuxPackage pkg) throws IOException {
|
||||
Path specFile = specFile(env, pkg);
|
||||
|
||||
// prepare spec file
|
||||
env.createResource(DEFAULT_SPEC_TEMPLATE)
|
||||
.setCategory(I18N.getString("resource.rpm-spec-file"))
|
||||
.setSubstitutionData(replacementData)
|
||||
.saveToFile(specFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Path buildPackageBundle(BuildEnv env, LinuxPackage pkg,
|
||||
Path outputParentDir) throws PackagerException, IOException {
|
||||
return buildRPM(env, pkg, outputParentDir);
|
||||
}
|
||||
|
||||
private static Path installPrefix(LinuxPackage pkg) {
|
||||
Path path = pkg.relativeInstallDir();
|
||||
if (!pkg.isInstallDirInUsrTree()) {
|
||||
path = path.getParent();
|
||||
}
|
||||
return Path.of("/").resolve(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> createReplacementData(BuildEnv env, LinuxPackage pkg) throws IOException {
|
||||
Map<String, String> data = new HashMap<>();
|
||||
|
||||
data.put("APPLICATION_RELEASE", pkg.release().orElseThrow());
|
||||
data.put("APPLICATION_PREFIX", installPrefix(pkg).toString());
|
||||
data.put("APPLICATION_DIRECTORY", Path.of("/").resolve(pkg.relativeInstallDir()).toString());
|
||||
data.put("APPLICATION_SUMMARY", pkg.app().name());
|
||||
data.put("APPLICATION_LICENSE_TYPE", ((LinuxRpmPackage)pkg).licenseType());
|
||||
|
||||
String licenseFile = pkg.licenseFile().map(v -> {
|
||||
return v.toAbsolutePath().normalize().toString();
|
||||
}).orElse(null);
|
||||
data.put("APPLICATION_LICENSE_FILE", licenseFile);
|
||||
data.put("APPLICATION_GROUP", pkg.category().orElse(""));
|
||||
|
||||
data.put("APPLICATION_URL", pkg.aboutURL().orElse(""));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initLibProvidersLookup(LibProvidersLookup libProvidersLookup) {
|
||||
libProvidersLookup.setPackageLookup(file -> {
|
||||
return Executor.of(TOOL_RPM,
|
||||
"-q", "--queryformat", "%{name}\\n",
|
||||
"-q", "--whatprovides", file.toString())
|
||||
.saveOutput(true).executeExpectSuccess().getOutput().stream();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ConfigException> verifyOutputBundle(BuildEnv env, LinuxPackage pkg,
|
||||
Path packageBundle) {
|
||||
List<ConfigException> errors = new ArrayList<>();
|
||||
|
||||
String specFileName = specFile(env, pkg).getFileName().toString();
|
||||
|
||||
try {
|
||||
List<PackageProperty> properties = List.of(
|
||||
new PackageProperty("Name", pkg.packageName(),
|
||||
"APPLICATION_PACKAGE", specFileName),
|
||||
new PackageProperty("Version", pkg.version(),
|
||||
"APPLICATION_VERSION", specFileName),
|
||||
new PackageProperty("Release", pkg.release().orElseThrow(),
|
||||
"APPLICATION_RELEASE", specFileName),
|
||||
new PackageProperty("Arch", pkg.arch(), null, specFileName));
|
||||
|
||||
List<String> actualValues = Executor.of(TOOL_RPM, "-qp", "--queryformat",
|
||||
properties.stream().map(entry -> String.format("%%{%s}",
|
||||
entry.name)).collect(Collectors.joining("\\n")),
|
||||
packageBundle.toString()).saveOutput(true).executeExpectSuccess().getOutput();
|
||||
|
||||
Iterator<String> actualValuesIt = actualValues.iterator();
|
||||
properties.forEach(property -> errors.add(property.verifyValue(
|
||||
actualValuesIt.next())));
|
||||
} catch (IOException ex) {
|
||||
// Ignore error as it is not critical. Just report it.
|
||||
Log.verbose(ex);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
private Path specFile(BuildEnv env, Package pkg) {
|
||||
return env.buildRoot().resolve(Path.of("SPECS", pkg.packageName() + ".spec"));
|
||||
}
|
||||
|
||||
private Path buildRPM(BuildEnv env, Package pkg, Path outdir) throws IOException {
|
||||
|
||||
Path rpmFile = outdir.toAbsolutePath().resolve(pkg.packageFileNameWithSuffix());
|
||||
|
||||
Log.verbose(I18N.format("message.outputting-bundle-location", rpmFile.getParent()));
|
||||
|
||||
//run rpmbuild
|
||||
Executor.of(TOOL_RPMBUILD,
|
||||
"-bb", specFile(env, pkg).toAbsolutePath().toString(),
|
||||
"--define", String.format("%%_sourcedir %s",
|
||||
env.appImageDir().toAbsolutePath()),
|
||||
// save result to output dir
|
||||
"--define", String.format("%%_rpmdir %s", rpmFile.getParent()),
|
||||
// do not use other system directories to build as current user
|
||||
"--define", String.format("%%_topdir %s",
|
||||
env.buildRoot().toAbsolutePath()),
|
||||
"--define", String.format("%%_rpmfilename %s", rpmFile.getFileName())
|
||||
).executeExpectSuccess();
|
||||
|
||||
Log.verbose(I18N.format("message.output-bundle-location", rpmFile.getParent()));
|
||||
|
||||
return rpmFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return I18N.getString("rpm.bundler.name");
|
||||
@@ -50,28 +219,12 @@ public class LinuxRpmBundler extends LinuxPackageBundler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path execute(Map<String, ? super Object> params, Path outputParentDir) throws PackagerException {
|
||||
|
||||
return Packager.<LinuxRpmPackage>build().outputDir(outputParentDir)
|
||||
.pkg(LinuxFromParams.RPM_PACKAGE.fetchFrom(params))
|
||||
.env(BuildEnvFromParams.BUILD_ENV.fetchFrom(params))
|
||||
.pipelineBuilderMutatorFactory((env, pkg, outputDir) -> {
|
||||
return new LinuxRpmPackager(env, pkg, outputDir, sysEnv.orElseThrow());
|
||||
}).execute(LinuxPackagingPipeline.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<LinuxRpmSystemEnvironment> sysEnv() {
|
||||
return sysEnv;
|
||||
public boolean supported(boolean runtimeInstaller) {
|
||||
return OperatingSystem.isLinux() && (createRpmbuildToolValidator().validate() == null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault() {
|
||||
return sysEnv.value()
|
||||
.map(LinuxSystemEnvironment::nativePackageType)
|
||||
.map(StandardPackageType.LINUX_RPM::equals)
|
||||
.orElse(false);
|
||||
return !LinuxDebBundler.isDebian();
|
||||
}
|
||||
|
||||
private final Result<LinuxRpmSystemEnvironment> sysEnv = LinuxRpmSystemEnvironment.create(SYS_ENV);
|
||||
}
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.LinuxRpmPackage;
|
||||
|
||||
|
||||
/**
|
||||
* There are two command line options to configure license information for RPM
|
||||
* packaging: --linux-rpm-license-type and --license-file. Value of
|
||||
* --linux-rpm-license-type command line option configures "License:" section
|
||||
* of RPM spec. Value of --license-file command line option specifies a license
|
||||
* file to be added to the package. License file is a sort of documentation file
|
||||
* but it will be installed even if user selects an option to install the
|
||||
* package without documentation. --linux-rpm-license-type is the primary option
|
||||
* to set license information. --license-file makes little sense in case of RPM
|
||||
* packaging.
|
||||
*/
|
||||
final class LinuxRpmPackager extends LinuxPackager<LinuxRpmPackage> {
|
||||
|
||||
LinuxRpmPackager(BuildEnv env, LinuxRpmPackage pkg, Path outputDir, LinuxRpmSystemEnvironment sysEnv) {
|
||||
super(env, pkg, outputDir, sysEnv);
|
||||
this.sysEnv = Objects.requireNonNull(sysEnv);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createConfigFiles(Map<String, String> replacementData) throws IOException {
|
||||
Path specFile = specFile();
|
||||
|
||||
// prepare spec file
|
||||
env.createResource("template.spec")
|
||||
.setCategory(I18N.getString("resource.rpm-spec-file"))
|
||||
.setSubstitutionData(replacementData)
|
||||
.saveToFile(specFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> createReplacementData() {
|
||||
Map<String, String> data = new HashMap<>();
|
||||
|
||||
data.put("APPLICATION_RELEASE", pkg.release().orElseThrow());
|
||||
data.put("APPLICATION_PREFIX", installPrefix().toString());
|
||||
data.put("APPLICATION_DIRECTORY", Path.of("/").resolve(pkg.relativeInstallDir()).toString());
|
||||
data.put("APPLICATION_SUMMARY", pkg.app().name());
|
||||
data.put("APPLICATION_LICENSE_TYPE", pkg.licenseType());
|
||||
|
||||
String licenseFile = pkg.licenseFile().map(v -> {
|
||||
return v.toAbsolutePath().normalize().toString();
|
||||
}).orElse(null);
|
||||
data.put("APPLICATION_LICENSE_FILE", licenseFile);
|
||||
data.put("APPLICATION_GROUP", pkg.category().orElse(""));
|
||||
|
||||
data.put("APPLICATION_URL", pkg.aboutURL().orElse(""));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initLibProvidersLookup(LibProvidersLookup libProvidersLookup) {
|
||||
libProvidersLookup.setPackageLookup(file -> {
|
||||
return Executor.of(sysEnv.rpm().toString(),
|
||||
"-q", "--queryformat", "%{name}\\n",
|
||||
"-q", "--whatprovides", file.toString()
|
||||
).saveOutput(true).executeExpectSuccess().getOutput().stream();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<? extends Exception> findErrorsInOutputPackage() throws IOException {
|
||||
List<ConfigException> errors = new ArrayList<>();
|
||||
|
||||
var specFileName = specFile().getFileName().toString();
|
||||
|
||||
var properties = List.of(
|
||||
new PackageProperty("Name", pkg.packageName(),
|
||||
"APPLICATION_PACKAGE", specFileName),
|
||||
new PackageProperty("Version", pkg.version(),
|
||||
"APPLICATION_VERSION", specFileName),
|
||||
new PackageProperty("Release", pkg.release().orElseThrow(),
|
||||
"APPLICATION_RELEASE", specFileName),
|
||||
new PackageProperty("Arch", pkg.arch(), null, specFileName));
|
||||
|
||||
var actualValues = Executor.of(
|
||||
sysEnv.rpm().toString(),
|
||||
"-qp",
|
||||
"--queryformat", properties.stream().map(e -> String.format("%%{%s}", e.name)).collect(joining("\\n")),
|
||||
outputPackageFile().toString()
|
||||
).saveOutput(true).executeExpectSuccess().getOutput();
|
||||
|
||||
for (int i = 0; i != properties.size(); i++) {
|
||||
Optional.ofNullable(properties.get(i).verifyValue(actualValues.get(i))).ifPresent(errors::add);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildPackage() throws IOException {
|
||||
|
||||
Path rpmFile = outputPackageFile();
|
||||
|
||||
Log.verbose(I18N.format("message.outputting-bundle-location", rpmFile.getParent()));
|
||||
|
||||
//run rpmbuild
|
||||
Executor.of(sysEnv.rpmbuild().toString(),
|
||||
"-bb", specFile().toAbsolutePath().toString(),
|
||||
"--define", String.format("%%_sourcedir %s",
|
||||
env.appImageDir().toAbsolutePath()),
|
||||
// save result to output dir
|
||||
"--define", String.format("%%_rpmdir %s", rpmFile.getParent()),
|
||||
// do not use other system directories to build as current user
|
||||
"--define", String.format("%%_topdir %s",
|
||||
env.buildRoot().toAbsolutePath()),
|
||||
"--define", String.format("%%_rpmfilename %s", rpmFile.getFileName())
|
||||
).executeExpectSuccess();
|
||||
|
||||
Log.verbose(I18N.format("message.output-bundle-location", rpmFile.getParent()));
|
||||
}
|
||||
|
||||
private Path installPrefix() {
|
||||
Path path = pkg.relativeInstallDir();
|
||||
if (!pkg.isInstallDirInUsrTree()) {
|
||||
path = path.getParent();
|
||||
}
|
||||
return Path.of("/").resolve(path);
|
||||
}
|
||||
|
||||
private Path specFile() {
|
||||
return env.buildRoot().resolve(Path.of("SPECS", pkg.packageName() + ".spec"));
|
||||
}
|
||||
|
||||
private final LinuxRpmSystemEnvironment sysEnv;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin;
|
||||
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxRpmSystemEnvironment extends LinuxSystemEnvironment, LinuxRpmSystemEnvironmentMixin {
|
||||
|
||||
static Result<LinuxRpmSystemEnvironment> create(Result<LinuxSystemEnvironment> base) {
|
||||
return mixin(LinuxRpmSystemEnvironment.class, base, LinuxRpmSystemEnvironmentMixin::create);
|
||||
}
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.model.DottedVersion;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxRpmSystemEnvironmentMixin {
|
||||
Path rpm();
|
||||
Path rpmbuild();
|
||||
|
||||
record Stub(Path rpm, Path rpmbuild) implements LinuxRpmSystemEnvironmentMixin {
|
||||
}
|
||||
|
||||
static Result<LinuxRpmSystemEnvironmentMixin> create() {
|
||||
|
||||
final var errors = Stream.of(
|
||||
Internal.createRpmbuildToolValidator(),
|
||||
new ToolValidator(Internal.TOOL_RPM)
|
||||
).map(ToolValidator::validate).filter(Objects::nonNull).toList();
|
||||
|
||||
if (errors.isEmpty()) {
|
||||
return Result.ofValue(new Stub(Internal.TOOL_RPM, Internal.TOOL_RPMBUILD));
|
||||
} else {
|
||||
return Result.ofErrors(errors);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Internal {
|
||||
private static ToolValidator createRpmbuildToolValidator() {
|
||||
Pattern pattern = Pattern.compile(" (\\d+\\.\\d+)");
|
||||
return new ToolValidator(TOOL_RPMBUILD).setMinimalVersion(
|
||||
TOOL_RPMBUILD_MIN_VERSION).setVersionParser(lines -> {
|
||||
String versionString = lines.limit(1).findFirst().orElseThrow();
|
||||
Matcher matcher = pattern.matcher(versionString);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private static final Path TOOL_RPM = Path.of("rpm");
|
||||
private static final Path TOOL_RPMBUILD = Path.of("rpmbuild");
|
||||
private static final DottedVersion TOOL_RPMBUILD_MIN_VERSION = DottedVersion.lazy("4.10");
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import jdk.jpackage.internal.model.PackageType;
|
||||
import jdk.jpackage.internal.model.StandardPackageType;
|
||||
import jdk.jpackage.internal.util.CompositeProxy;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxSystemEnvironment extends SystemEnvironment {
|
||||
boolean soLookupAvailable();
|
||||
PackageType nativePackageType();
|
||||
|
||||
static Result<LinuxSystemEnvironment> create() {
|
||||
return detectNativePackageType().map(LinuxSystemEnvironment::create).orElseGet(() -> {
|
||||
return Result.ofError(new RuntimeException("Unknown native package type"));
|
||||
});
|
||||
}
|
||||
|
||||
static Optional<PackageType> detectNativePackageType() {
|
||||
if (Internal.isDebian()) {
|
||||
return Optional.of(StandardPackageType.LINUX_DEB);
|
||||
} else if (Internal.isRpm()) {
|
||||
return Optional.of(StandardPackageType.LINUX_RPM);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
static Result<LinuxSystemEnvironment> create(PackageType nativePackageType) {
|
||||
return Result.ofValue(new Stub(LibProvidersLookup.supported(),
|
||||
Objects.requireNonNull(nativePackageType)));
|
||||
}
|
||||
|
||||
static <T, U extends LinuxSystemEnvironment> U createWithMixin(Class<U> type, LinuxSystemEnvironment base, T mixin) {
|
||||
return CompositeProxy.create(type, base, mixin);
|
||||
}
|
||||
|
||||
static <T, U extends LinuxSystemEnvironment> Result<U> mixin(Class<U> type,
|
||||
Result<LinuxSystemEnvironment> base, Supplier<Result<T>> mixinResultSupplier) {
|
||||
final var mixin = mixinResultSupplier.get();
|
||||
|
||||
final List<Exception> errors = new ArrayList<>();
|
||||
errors.addAll(base.errors());
|
||||
errors.addAll(mixin.errors());
|
||||
|
||||
if (errors.isEmpty()) {
|
||||
return Result.ofValue(createWithMixin(type, base.orElseThrow(), mixin.orElseThrow()));
|
||||
} else {
|
||||
return Result.ofErrors(errors);
|
||||
}
|
||||
}
|
||||
|
||||
record Stub(boolean soLookupAvailable, PackageType nativePackageType) implements LinuxSystemEnvironment {
|
||||
}
|
||||
|
||||
static final class Internal {
|
||||
|
||||
private static boolean isDebian() {
|
||||
// we are just going to run "dpkg -s coreutils" and assume Debian
|
||||
// or derivative if no error is returned.
|
||||
try {
|
||||
Executor.of("dpkg", "-s", "coreutils").executeExpectSuccess();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
// just fall thru
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isRpm() {
|
||||
// we are just going to run "rpm -q rpm" and assume RPM
|
||||
// or derivative if no error is returned.
|
||||
try {
|
||||
Executor.of("rpm", "-q", "rpm").executeExpectSuccess();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
// just fall thru
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,12 @@
|
||||
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.MacDmgPackage;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public class MacDmgBundler extends MacBaseInstallerBundler {
|
||||
|
||||
@@ -72,27 +70,39 @@ public class MacDmgBundler extends MacBaseInstallerBundler {
|
||||
public Path execute(Map<String, ? super Object> params,
|
||||
Path outputParentDir) throws PackagerException {
|
||||
|
||||
var pkg = MacFromParams.DMG_PACKAGE.fetchFrom(params);
|
||||
final var pkg = MacFromParams.DMG_PACKAGE.fetchFrom(params);
|
||||
var env = MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params);
|
||||
|
||||
Log.verbose(I18N.format("message.building-dmg", pkg.app().name()));
|
||||
final var packager = MacDmgPackager.build().outputDir(outputParentDir).pkg(pkg).env(env);
|
||||
|
||||
return Packager.<MacDmgPackage>build().outputDir(outputParentDir)
|
||||
.pkg(pkg)
|
||||
.env(MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params))
|
||||
.pipelineBuilderMutatorFactory((env, _, outputDir) -> {
|
||||
return new MacDmgPackager(env, pkg, outputDir, sysEnv.orElseThrow());
|
||||
}).execute(MacPackagingPipeline.build(Optional.of(pkg)));
|
||||
MacDmgPackager.findSetFileUtility().ifPresent(packager::setFileUtility);
|
||||
|
||||
return packager.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supported(boolean runtimeInstaller) {
|
||||
return sysEnv.hasValue();
|
||||
return isSupported();
|
||||
}
|
||||
|
||||
public static final String[] required =
|
||||
{"/usr/bin/hdiutil", "/usr/bin/osascript"};
|
||||
public static boolean isSupported() {
|
||||
try {
|
||||
for (String s : required) {
|
||||
Path f = Path.of(s);
|
||||
if (!Files.exists(f) || !Files.isExecutable(f)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private final Result<MacDmgSystemEnvironment> sysEnv = MacDmgSystemEnvironment.create();
|
||||
}
|
||||
|
||||
@@ -36,25 +36,106 @@ import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PackageTaskID;
|
||||
import jdk.jpackage.internal.PackagingPipeline.StartupParameters;
|
||||
import jdk.jpackage.internal.PackagingPipeline.TaskID;
|
||||
import jdk.jpackage.internal.model.MacDmgPackage;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
import jdk.jpackage.internal.util.FileUtils;
|
||||
import jdk.jpackage.internal.util.PathGroup;
|
||||
|
||||
record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
MacDmgSystemEnvironment sysEnv) implements Consumer<PackagingPipeline.Builder> {
|
||||
record MacDmgPackager(MacDmgPackage pkg, BuildEnv env, Path hdiutil, Path outputDir, Optional<Path> setFileUtility) {
|
||||
|
||||
MacDmgPackager {
|
||||
Objects.requireNonNull(env);
|
||||
Objects.requireNonNull(pkg);
|
||||
Objects.requireNonNull(env);
|
||||
Objects.requireNonNull(hdiutil);
|
||||
Objects.requireNonNull(outputDir);
|
||||
Objects.requireNonNull(sysEnv);
|
||||
Objects.requireNonNull(setFileUtility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(PackagingPipeline.Builder pipelineBuilder) {
|
||||
static Builder build() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
static final class Builder extends PackagerBuilder<MacDmgPackage, Builder> {
|
||||
|
||||
Builder hdiutil(Path v) {
|
||||
hdiutil = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder setFileUtility(Path v) {
|
||||
setFileUtility = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
Path execute() throws PackagerException {
|
||||
Log.verbose(MessageFormat.format(I18N.getString("message.building-dmg"),
|
||||
pkg.app().name()));
|
||||
|
||||
IOUtils.writableOutputDir(outputDir);
|
||||
|
||||
return execute(MacPackagingPipeline.build(Optional.of(pkg)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configurePackagingPipeline(PackagingPipeline.Builder pipelineBuilder,
|
||||
StartupParameters startupParameters) {
|
||||
final var packager = new MacDmgPackager(pkg, startupParameters.packagingEnv(),
|
||||
validatedHdiutil(), outputDir, Optional.ofNullable(setFileUtility));
|
||||
packager.applyToPipeline(pipelineBuilder);
|
||||
}
|
||||
|
||||
private Path validatedHdiutil() {
|
||||
return Optional.ofNullable(hdiutil).orElse(HDIUTIL);
|
||||
}
|
||||
|
||||
private Path hdiutil;
|
||||
private Path setFileUtility;
|
||||
}
|
||||
|
||||
// Location of SetFile utility may be different depending on MacOS version
|
||||
// We look for several known places and if none of them work will
|
||||
// try to find it
|
||||
static Optional<Path> findSetFileUtility() {
|
||||
String typicalPaths[] = {"/Developer/Tools/SetFile",
|
||||
"/usr/bin/SetFile", "/Developer/usr/bin/SetFile"};
|
||||
|
||||
final var setFilePath = Stream.of(typicalPaths).map(Path::of).filter(Files::isExecutable).findFirst();
|
||||
if (setFilePath.isPresent()) {
|
||||
// Validate SetFile, if Xcode is not installed it will run, but exit with error
|
||||
// code
|
||||
try {
|
||||
if (Executor.of(setFilePath.orElseThrow().toString(), "-h").setQuiet(true).execute() == 0) {
|
||||
return setFilePath;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// No need for generic find attempt. We found it, but it does not work.
|
||||
// Probably due to missing xcode.
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
// generic find attempt
|
||||
try {
|
||||
final var executor = Executor.of("/usr/bin/xcrun", "-find", "SetFile");
|
||||
final var code = executor.setQuiet(true).saveOutput(true).execute();
|
||||
if (code == 0 && executor.getOutput().isEmpty()) {
|
||||
final var firstLine = executor.getOutput().getFirst();
|
||||
Path f = Path.of(firstLine);
|
||||
if (Files.exists(f) && Files.isExecutable(f)) {
|
||||
return Optional.of(f.toAbsolutePath());
|
||||
}
|
||||
}
|
||||
} catch (IOException ignored) {}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private void applyToPipeline(PackagingPipeline.Builder pipelineBuilder) {
|
||||
pipelineBuilder
|
||||
.excludeDirFromCopying(outputDir)
|
||||
.task(DmgPackageTaskID.COPY_DMG_CONTENT)
|
||||
@@ -237,7 +318,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
|
||||
// create temp image
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
hdiutil.toString(),
|
||||
"create",
|
||||
hdiUtilVerbosityFlag,
|
||||
"-srcfolder", normalizedAbsolutePathString(srcFolder),
|
||||
@@ -260,7 +341,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
// We need extra room for icons and background image. When we providing
|
||||
// actual files to hdiutil, it will create DMG with ~50 megabytes extra room.
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
hdiutil.toString(),
|
||||
"create",
|
||||
hdiUtilVerbosityFlag,
|
||||
"-size", String.valueOf(size),
|
||||
@@ -276,7 +357,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
|
||||
// mount temp image
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
hdiutil.toString(),
|
||||
"attach",
|
||||
normalizedAbsolutePathString(protoDMG),
|
||||
hdiUtilVerbosityFlag,
|
||||
@@ -301,7 +382,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
// to install-dir in DMG as critical error, since it can fail in
|
||||
// headless environment.
|
||||
try {
|
||||
pb = new ProcessBuilder(sysEnv.osascript().toString(),
|
||||
pb = new ProcessBuilder("/usr/bin/osascript",
|
||||
normalizedAbsolutePathString(volumeScript()));
|
||||
IOUtils.exec(pb, 180); // Wait 3 minutes. See JDK-8248248.
|
||||
} catch (IOException ex) {
|
||||
@@ -316,7 +397,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
// NB: attributes of the root directory are ignored
|
||||
// when creating the volume
|
||||
// Therefore we have to do this after we mount image
|
||||
if (sysEnv.setFileUtility().isPresent()) {
|
||||
if (setFileUtility.isPresent()) {
|
||||
//can not find utility => keep going without icon
|
||||
try {
|
||||
volumeIconFile.toFile().setWritable(true);
|
||||
@@ -325,14 +406,14 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
// "icnC" for the volume icon
|
||||
// (might not work on Mac 10.13 with old XCode)
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.setFileUtility().orElseThrow().toString(),
|
||||
setFileUtility.orElseThrow().toString(),
|
||||
"-c", "icnC",
|
||||
normalizedAbsolutePathString(volumeIconFile));
|
||||
IOUtils.exec(pb);
|
||||
volumeIconFile.toFile().setReadOnly();
|
||||
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.setFileUtility().orElseThrow().toString(),
|
||||
setFileUtility.orElseThrow().toString(),
|
||||
"-a", "C",
|
||||
normalizedAbsolutePathString(mountedVolume));
|
||||
IOUtils.exec(pb);
|
||||
@@ -347,7 +428,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
} finally {
|
||||
// Detach the temporary image
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
hdiutil.toString(),
|
||||
"detach",
|
||||
hdiUtilVerbosityFlag,
|
||||
normalizedAbsolutePathString(mountedVolume));
|
||||
@@ -370,7 +451,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
// Now force to detach if it still attached
|
||||
if (Files.exists(mountedVolume)) {
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
hdiutil.toString(),
|
||||
"detach",
|
||||
"-force",
|
||||
hdiUtilVerbosityFlag,
|
||||
@@ -383,7 +464,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
|
||||
// Compress it to a new image
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
hdiutil.toString(),
|
||||
"convert",
|
||||
normalizedAbsolutePathString(protoDMG),
|
||||
hdiUtilVerbosityFlag,
|
||||
@@ -400,7 +481,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
Files.copy(protoDMG, protoCopyDMG);
|
||||
try {
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
hdiutil.toString(),
|
||||
"convert",
|
||||
normalizedAbsolutePathString(protoCopyDMG),
|
||||
hdiUtilVerbosityFlag,
|
||||
@@ -415,7 +496,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
//add license if needed
|
||||
if (pkg.licenseFile().isPresent()) {
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
hdiutil.toString(),
|
||||
"udifrez",
|
||||
normalizedAbsolutePathString(finalDMG),
|
||||
"-xml",
|
||||
@@ -446,4 +527,6 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
private static final String TEMPLATE_BUNDLE_ICON = "JavaApp.icns";
|
||||
|
||||
private static final String DEFAULT_LICENSE_PLIST="lic_template.plist";
|
||||
|
||||
private static final Path HDIUTIL = Path.of("/usr/bin/hdiutil");
|
||||
}
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
record MacDmgSystemEnvironment(Path hdiutil, Path osascript, Optional<Path> setFileUtility) implements SystemEnvironment {
|
||||
|
||||
MacDmgSystemEnvironment {
|
||||
}
|
||||
|
||||
static Result<MacDmgSystemEnvironment> create() {
|
||||
final var errors = Stream.of(HDIUTIL, OSASCRIPT)
|
||||
.map(ToolValidator::new)
|
||||
.map(ToolValidator::checkExistsOnly)
|
||||
.map(ToolValidator::validate)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
if (errors.isEmpty()) {
|
||||
return Result.ofValue(new MacDmgSystemEnvironment(HDIUTIL, OSASCRIPT, findSetFileUtility()));
|
||||
} else {
|
||||
return Result.ofErrors(errors);
|
||||
}
|
||||
}
|
||||
|
||||
// Location of SetFile utility may be different depending on MacOS version
|
||||
// We look for several known places and if none of them work will
|
||||
// try to find it
|
||||
private static Optional<Path> findSetFileUtility() {
|
||||
String typicalPaths[] = {"/Developer/Tools/SetFile",
|
||||
"/usr/bin/SetFile", "/Developer/usr/bin/SetFile"};
|
||||
|
||||
final var setFilePath = Stream.of(typicalPaths).map(Path::of).filter(Files::isExecutable).findFirst();
|
||||
if (setFilePath.isPresent()) {
|
||||
// Validate SetFile, if Xcode is not installed it will run, but exit with error
|
||||
// code
|
||||
try {
|
||||
if (Executor.of(setFilePath.orElseThrow().toString(), "-h").setQuiet(true).execute() == 0) {
|
||||
return setFilePath;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// No need for generic find attempt. We found it, but it does not work.
|
||||
// Probably due to missing xcode.
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
// generic find attempt
|
||||
try {
|
||||
final var executor = Executor.of("/usr/bin/xcrun", "-find", "SetFile");
|
||||
final var code = executor.setQuiet(true).saveOutput(true).execute();
|
||||
if (code == 0 && !executor.getOutput().isEmpty()) {
|
||||
final var firstLine = executor.getOutput().getFirst();
|
||||
Path f = Path.of(firstLine);
|
||||
if (new ToolValidator(f).checkExistsOnly().validate() == null) {
|
||||
return Optional.of(f.toAbsolutePath());
|
||||
}
|
||||
}
|
||||
} catch (IOException ignored) {}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static final Path HDIUTIL = Path.of("/usr/bin/hdiutil");
|
||||
private static final Path OSASCRIPT = Path.of("/usr/bin/osascript");
|
||||
}
|
||||
@@ -28,9 +28,7 @@ package jdk.jpackage.internal;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.MacPkgPackage;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
|
||||
public class MacPkgBundler extends MacBaseInstallerBundler {
|
||||
@@ -51,7 +49,7 @@ public class MacPkgBundler extends MacBaseInstallerBundler {
|
||||
try {
|
||||
Objects.requireNonNull(params);
|
||||
|
||||
final var pkg = MacFromParams.PKG_PACKAGE.fetchFrom(params);
|
||||
final var pkgPkg = MacFromParams.PKG_PACKAGE.fetchFrom(params);
|
||||
|
||||
// run basic validation to ensure requirements are met
|
||||
// we are not interested in return code, only possible exception
|
||||
@@ -74,16 +72,12 @@ public class MacPkgBundler extends MacBaseInstallerBundler {
|
||||
public Path execute(Map<String, ? super Object> params,
|
||||
Path outputParentDir) throws PackagerException {
|
||||
|
||||
var pkg = MacFromParams.PKG_PACKAGE.fetchFrom(params);
|
||||
final var pkg = MacFromParams.PKG_PACKAGE.fetchFrom(params);
|
||||
var env = MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params);
|
||||
|
||||
Log.verbose(I18N.format("message.building-pkg", pkg.app().name()));
|
||||
final var packager = MacPkgPackager.build().outputDir(outputParentDir).pkg(pkg).env(env);
|
||||
|
||||
return Packager.<MacPkgPackage>build().outputDir(outputParentDir)
|
||||
.pkg(pkg)
|
||||
.env(MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params))
|
||||
.pipelineBuilderMutatorFactory((env, _, outputDir) -> {
|
||||
return new MacPkgPackager(env, pkg, outputDir);
|
||||
}).execute(MacPackagingPipeline.build(Optional.of(pkg)));
|
||||
return packager.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,7 +41,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
@@ -53,25 +52,15 @@ import javax.xml.transform.stream.StreamSource;
|
||||
import jdk.internal.util.Architecture;
|
||||
import jdk.internal.util.OSVersion;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PackageTaskID;
|
||||
import jdk.jpackage.internal.PackagingPipeline.StartupParameters;
|
||||
import jdk.jpackage.internal.PackagingPipeline.TaskID;
|
||||
import jdk.jpackage.internal.model.MacPkgPackage;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
import jdk.jpackage.internal.resources.ResourceLocator;
|
||||
import jdk.jpackage.internal.util.XmlUtils;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional<Services> services,
|
||||
Path outputDir) implements Consumer<PackagingPipeline.Builder> {
|
||||
|
||||
MacPkgPackager {
|
||||
Objects.requireNonNull(env);
|
||||
Objects.requireNonNull(pkg);
|
||||
Objects.requireNonNull(services);
|
||||
Objects.requireNonNull(outputDir);
|
||||
}
|
||||
|
||||
MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Path outputDir) {
|
||||
this(env, pkg, createServices(env, pkg), outputDir);
|
||||
}
|
||||
record MacPkgPackager(MacPkgPackage pkg, BuildEnv env, Optional<Services> services, Path outputDir) {
|
||||
|
||||
enum PkgPackageTaskID implements TaskID {
|
||||
PREPARE_MAIN_SCRIPTS,
|
||||
@@ -80,6 +69,37 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional<Services> servic
|
||||
PREPARE_SERVICES
|
||||
}
|
||||
|
||||
static Builder build() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
static final class Builder extends PackagerBuilder<MacPkgPackage, Builder> {
|
||||
|
||||
Path execute() throws PackagerException {
|
||||
Log.verbose(MessageFormat.format(I18N.getString("message.building-pkg"),
|
||||
pkg.app().name()));
|
||||
|
||||
IOUtils.writableOutputDir(outputDir);
|
||||
|
||||
return execute(MacPackagingPipeline.build(Optional.of(pkg)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configurePackagingPipeline(PackagingPipeline.Builder pipelineBuilder,
|
||||
StartupParameters startupParameters) {
|
||||
final var packager = new MacPkgPackager(pkg, startupParameters.packagingEnv(), createServices(), outputDir);
|
||||
packager.applyToPipeline(pipelineBuilder);
|
||||
}
|
||||
|
||||
private Optional<Services> createServices() {
|
||||
if (pkg.app().isService()) {
|
||||
return Optional.of(Services.create(pkg, env));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record InternalPackage(Path srcRoot, String identifier, Path path, List<String> otherPkgbuildArgs) {
|
||||
|
||||
InternalPackage {
|
||||
@@ -210,8 +230,7 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional<Services> servic
|
||||
private final Optional<String> nameSuffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(PackagingPipeline.Builder pipelineBuilder) {
|
||||
private void applyToPipeline(PackagingPipeline.Builder pipelineBuilder) {
|
||||
pipelineBuilder
|
||||
.excludeDirFromCopying(outputDir)
|
||||
.task(PkgPackageTaskID.PREPARE_MAIN_SCRIPTS)
|
||||
@@ -540,14 +559,6 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional<Services> servic
|
||||
IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT);
|
||||
}
|
||||
|
||||
private static Optional<Services> createServices(BuildEnv env, MacPkgPackage pkg) {
|
||||
if (pkg.app().isService()) {
|
||||
return Optional.of(Services.create(pkg, env));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String DEFAULT_BACKGROUND_IMAGE = "background_pkg.png";
|
||||
private static final String DEFAULT_PDF = "product-def.plist";
|
||||
}
|
||||
|
||||
+17
-47
@@ -26,80 +26,50 @@ package jdk.jpackage.internal;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import jdk.jpackage.internal.PackagingPipeline.StartupParameters;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
|
||||
final class Packager<T extends Package> {
|
||||
abstract class PackagerBuilder<T extends Package, U extends PackagerBuilder<T, U>> {
|
||||
|
||||
static <T extends Package> Packager<T> build() {
|
||||
return new Packager<>();
|
||||
}
|
||||
|
||||
Packager<T> pkg(T v) {
|
||||
U pkg(T v) {
|
||||
pkg = v;
|
||||
return this;
|
||||
return thiz();
|
||||
}
|
||||
|
||||
Packager<T> env(BuildEnv v) {
|
||||
U env(BuildEnv v) {
|
||||
env = v;
|
||||
return this;
|
||||
return thiz();
|
||||
}
|
||||
|
||||
Packager<T> outputDir(Path v) {
|
||||
U outputDir(Path v) {
|
||||
outputDir = v;
|
||||
return this;
|
||||
return thiz();
|
||||
}
|
||||
|
||||
Packager<T> pipelineBuilderMutatorFactory(PipelineBuilderMutatorFactory<T> v) {
|
||||
pipelineBuilderMutatorFactory = v;
|
||||
return this;
|
||||
@SuppressWarnings("unchecked")
|
||||
private U thiz() {
|
||||
return (U)this;
|
||||
}
|
||||
|
||||
T pkg() {
|
||||
return Objects.requireNonNull(pkg);
|
||||
}
|
||||
|
||||
Path outputDir() {
|
||||
return Objects.requireNonNull(outputDir);
|
||||
}
|
||||
|
||||
BuildEnv env() {
|
||||
return Objects.requireNonNull(env);
|
||||
}
|
||||
protected abstract void configurePackagingPipeline(PackagingPipeline.Builder pipelineBuilder,
|
||||
StartupParameters startupParameters);
|
||||
|
||||
Path execute(PackagingPipeline.Builder pipelineBuilder) throws PackagerException {
|
||||
Objects.requireNonNull(pkg);
|
||||
Objects.requireNonNull(env);
|
||||
Objects.requireNonNull(outputDir);
|
||||
|
||||
IOUtils.writableOutputDir(outputDir);
|
||||
|
||||
final var startupParameters = pipelineBuilder.createStartupParameters(env, pkg, outputDir);
|
||||
|
||||
pipelineBuilderMutatorFactory().ifPresent(factory -> {
|
||||
factory.create(startupParameters.packagingEnv(), pkg, outputDir).accept(pipelineBuilder);
|
||||
});
|
||||
configurePackagingPipeline(pipelineBuilder, startupParameters);
|
||||
|
||||
pipelineBuilder.create().execute(startupParameters);
|
||||
|
||||
return outputDir.resolve(pkg.packageFileNameWithSuffix());
|
||||
}
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
interface PipelineBuilderMutatorFactory<T extends Package> {
|
||||
Consumer<PackagingPipeline.Builder> create(BuildEnv env, T pkg, Path outputDir);
|
||||
}
|
||||
|
||||
|
||||
private Optional<PipelineBuilderMutatorFactory<T>> pipelineBuilderMutatorFactory() {
|
||||
return Optional.ofNullable(pipelineBuilderMutatorFactory);
|
||||
}
|
||||
|
||||
private T pkg;
|
||||
private BuildEnv env;
|
||||
private Path outputDir;
|
||||
private PipelineBuilderMutatorFactory<T> pipelineBuilderMutatorFactory;
|
||||
protected T pkg;
|
||||
protected BuildEnv env;
|
||||
protected Path outputDir;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
public interface SystemEnvironment {
|
||||
}
|
||||
@@ -24,31 +24,36 @@
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.DottedVersion;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
final class ToolValidator {
|
||||
public final class ToolValidator {
|
||||
|
||||
ToolValidator(String tool) {
|
||||
this(Path.of(tool));
|
||||
}
|
||||
|
||||
ToolValidator(Path toolPath) {
|
||||
this.toolPath = Objects.requireNonNull(toolPath);
|
||||
this.toolPath = toolPath;
|
||||
args = new ArrayList<>();
|
||||
|
||||
if (OperatingSystem.isLinux()) {
|
||||
setCommandLine("--version");
|
||||
}
|
||||
|
||||
setToolNotFoundErrorHandler(null);
|
||||
setToolOldVersionErrorHandler(null);
|
||||
}
|
||||
|
||||
ToolValidator setCommandLine(String... args) {
|
||||
@@ -62,17 +67,7 @@ final class ToolValidator {
|
||||
}
|
||||
|
||||
ToolValidator setMinimalVersion(DottedVersion v) {
|
||||
return setMinimalVersion(new Comparable<String>() {
|
||||
@Override
|
||||
public int compareTo(String o) {
|
||||
return DottedVersion.compareComponents(v, DottedVersion.lazy(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return v.toString();
|
||||
}
|
||||
});
|
||||
return setMinimalVersion(t -> DottedVersion.compareComponents(v, DottedVersion.lazy(t)));
|
||||
}
|
||||
|
||||
ToolValidator setVersionParser(Function<Stream<String>, String> v) {
|
||||
@@ -80,85 +75,69 @@ final class ToolValidator {
|
||||
return this;
|
||||
}
|
||||
|
||||
ToolValidator setToolNotFoundErrorHandler(Function<Path, ConfigException> v) {
|
||||
ToolValidator setToolNotFoundErrorHandler(
|
||||
BiFunction<String, IOException, ConfigException> v) {
|
||||
toolNotFoundErrorHandler = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
ToolValidator setToolOldVersionErrorHandler(BiFunction<Path, String, ConfigException> v) {
|
||||
ToolValidator setToolOldVersionErrorHandler(BiFunction<String, String, ConfigException> v) {
|
||||
toolOldVersionErrorHandler = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
ToolValidator checkExistsOnly(boolean v) {
|
||||
checkExistsOnly = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
ToolValidator checkExistsOnly() {
|
||||
return checkExistsOnly(true);
|
||||
}
|
||||
|
||||
ConfigException validate() {
|
||||
if (checkExistsOnly) {
|
||||
if (Files.isExecutable(toolPath) && !Files.isDirectory(toolPath)) {
|
||||
return null;
|
||||
} else if (Files.exists(toolPath)) {
|
||||
return new ConfigException(
|
||||
I18N.format("error.tool-not-executable", toolPath), (String)null);
|
||||
} else if (toolNotFoundErrorHandler != null) {
|
||||
return toolNotFoundErrorHandler.apply(toolPath);
|
||||
} else {
|
||||
return new ConfigException(
|
||||
I18N.format("error.tool-not-found", toolPath),
|
||||
I18N.format("error.tool-not-found.advice", toolPath));
|
||||
}
|
||||
}
|
||||
|
||||
List<String> cmdline = new ArrayList<>();
|
||||
cmdline.add(toolPath.toString());
|
||||
if (args != null) {
|
||||
cmdline.addAll(args);
|
||||
}
|
||||
|
||||
boolean canUseTool[] = new boolean[1];
|
||||
if (minimalVersion == null) {
|
||||
// No version check.
|
||||
canUseTool[0] = true;
|
||||
}
|
||||
|
||||
String[] version = new String[1];
|
||||
cmdline.addAll(args);
|
||||
|
||||
String name = IOUtils.getFileName(toolPath).toString();
|
||||
try {
|
||||
Executor.of(cmdline.toArray(String[]::new)).setQuiet(true).setOutputConsumer(lines -> {
|
||||
ProcessBuilder pb = new ProcessBuilder(cmdline);
|
||||
AtomicBoolean canUseTool = new AtomicBoolean();
|
||||
if (minimalVersion == null) {
|
||||
// No version check.
|
||||
canUseTool.setPlain(true);
|
||||
}
|
||||
|
||||
String[] version = new String[1];
|
||||
Executor.of(pb).setQuiet(true).setOutputConsumer(lines -> {
|
||||
if (versionParser != null && minimalVersion != null) {
|
||||
version[0] = versionParser.apply(lines);
|
||||
if (version[0] != null && minimalVersion.compareTo(version[0]) <= 0) {
|
||||
canUseTool[0] = true;
|
||||
if (minimalVersion.compareTo(version[0]) < 0) {
|
||||
canUseTool.setPlain(true);
|
||||
}
|
||||
}
|
||||
}).execute();
|
||||
|
||||
if (!canUseTool.getPlain()) {
|
||||
if (toolOldVersionErrorHandler != null) {
|
||||
return toolOldVersionErrorHandler.apply(name, version[0]);
|
||||
}
|
||||
return new ConfigException(MessageFormat.format(I18N.getString(
|
||||
"error.tool-old-version"), name, minimalVersion),
|
||||
MessageFormat.format(I18N.getString(
|
||||
"error.tool-old-version.advice"), name,
|
||||
minimalVersion));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return new ConfigException(I18N.format("error.tool-error", toolPath, e.getMessage()), null, e);
|
||||
if (toolNotFoundErrorHandler != null) {
|
||||
return toolNotFoundErrorHandler.apply(name, e);
|
||||
}
|
||||
return new ConfigException(MessageFormat.format(I18N.getString(
|
||||
"error.tool-not-found"), name, e.getMessage()),
|
||||
MessageFormat.format(I18N.getString(
|
||||
"error.tool-not-found.advice"), name), e);
|
||||
}
|
||||
|
||||
if (canUseTool[0]) {
|
||||
// All good. Tool can be used.
|
||||
return null;
|
||||
} else if (toolOldVersionErrorHandler != null) {
|
||||
return toolOldVersionErrorHandler.apply(toolPath, version[0]);
|
||||
} else {
|
||||
return new ConfigException(
|
||||
I18N.format("error.tool-old-version", toolPath, minimalVersion),
|
||||
I18N.format("error.tool-old-version.advice", toolPath, minimalVersion));
|
||||
}
|
||||
// All good. Tool can be used.
|
||||
return null;
|
||||
}
|
||||
|
||||
private final Path toolPath;
|
||||
private List<String> args;
|
||||
private Comparable<String> minimalVersion;
|
||||
private Function<Stream<String>, String> versionParser;
|
||||
private Function<Path, ConfigException> toolNotFoundErrorHandler;
|
||||
private BiFunction<Path, String, ConfigException> toolOldVersionErrorHandler;
|
||||
private boolean checkExistsOnly;
|
||||
private BiFunction<String, IOException, ConfigException> toolNotFoundErrorHandler;
|
||||
private BiFunction<String, String, ConfigException> toolOldVersionErrorHandler;
|
||||
}
|
||||
|
||||
+4
-7
@@ -66,13 +66,10 @@ error.no-content-types-for-file-association.advice=Specify MIME type for File As
|
||||
error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}
|
||||
error.too-many-content-types-for-file-association.advice=Specify only one MIME type for File Association number {0}
|
||||
|
||||
error.tool-error=Can not validate "{0}". Reason: {1}
|
||||
error.tool-not-executable="{0}" is not executable
|
||||
error.tool-not-found=Can not find "{0}"
|
||||
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.tool-not-found=Can not find {0}. Reason: {1}
|
||||
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.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
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.ExceptionBox.rethrowUnchecked;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
|
||||
public record Result<T>(Optional<T> value, Collection<? extends Exception> errors) {
|
||||
public Result {
|
||||
if (value.isEmpty() == errors.isEmpty()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (value.isEmpty() && errors.isEmpty()) {
|
||||
throw new IllegalArgumentException("Error collection must be non-empty");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public T orElseThrow() {
|
||||
firstError().ifPresent(ex -> {
|
||||
rethrowUnchecked(ex);
|
||||
});
|
||||
return value.orElseThrow();
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return value.isPresent();
|
||||
}
|
||||
|
||||
public boolean hasErrors() {
|
||||
return !errors.isEmpty();
|
||||
}
|
||||
|
||||
public <U> Result<U> map(Function<T, U> conv) {
|
||||
return new Result<>(value.map(conv), errors);
|
||||
}
|
||||
|
||||
public <U> Result<U> flatMap(Function<T, Result<U>> conv) {
|
||||
return value.map(conv).orElseGet(() -> {
|
||||
return new Result<>(Optional.empty(), errors);
|
||||
});
|
||||
}
|
||||
|
||||
public Result<T> mapErrors(UnaryOperator<Collection<? extends Exception>> errorsMapper) {
|
||||
return new Result<>(value, errorsMapper.apply(errors));
|
||||
}
|
||||
|
||||
public <U> Result<U> mapErrors() {
|
||||
return new Result<>(Optional.empty(), errors);
|
||||
}
|
||||
|
||||
public Result<T> peekErrors(Consumer<Collection<? extends Exception>> consumer) {
|
||||
if (hasErrors()) {
|
||||
consumer.accept(errors);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Result<T> peekValue(Consumer<T> consumer) {
|
||||
value.ifPresent(consumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<? extends Exception> firstError() {
|
||||
return errors.stream().findFirst();
|
||||
}
|
||||
|
||||
public static <T> Result<T> create(Supplier<T> supplier) {
|
||||
try {
|
||||
return ofValue(supplier.get());
|
||||
} catch (Exception ex) {
|
||||
return ofError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Result<T> ofValue(T value) {
|
||||
return new Result<>(Optional.of(value), List.of());
|
||||
}
|
||||
|
||||
public static <T> Result<T> ofErrors(Collection<? extends Exception> errors) {
|
||||
return new Result<>(Optional.empty(), List.copyOf(errors));
|
||||
}
|
||||
|
||||
public static <T> Result<T> ofError(Exception error) {
|
||||
return ofErrors(List.of(error));
|
||||
}
|
||||
|
||||
public static boolean allHaveValues(Iterable<? extends Result<?>> results) {
|
||||
return StreamSupport.stream(results.spliterator(), false).allMatch(Result::hasValue);
|
||||
}
|
||||
|
||||
public static boolean allHaveValues(Result<?>... results) {
|
||||
return allHaveValues(List.of(results));
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,17 @@
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.ICON;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Map;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
import jdk.jpackage.internal.model.WinMsiPackage;
|
||||
import jdk.jpackage.internal.model.WinExePackage;
|
||||
|
||||
@SuppressWarnings("restricted")
|
||||
public class WinExeBundler extends AbstractBundler {
|
||||
@@ -73,23 +79,63 @@ public class WinExeBundler extends AbstractBundler {
|
||||
throws PackagerException {
|
||||
|
||||
// Order is important!
|
||||
var pkg = WinFromParams.EXE_PACKAGE.fetchFrom(params);
|
||||
var pkg = WinFromParams.MSI_PACKAGE.fetchFrom(params);
|
||||
var env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params);
|
||||
|
||||
var msiOutputDir = env.buildRoot().resolve("msi");
|
||||
IOUtils.writableOutputDir(outdir);
|
||||
|
||||
return Packager.<WinMsiPackage>build().outputDir(msiOutputDir)
|
||||
.pkg(pkg.msiPackage())
|
||||
.env(env)
|
||||
.pipelineBuilderMutatorFactory((packagingEnv, msiPackage, _) -> {
|
||||
var msiPackager = new WinMsiPackager(packagingEnv, msiPackage,
|
||||
msiOutputDir, msiBundler.sysEnv.orElseThrow());
|
||||
var exePackager = new WinExePackager(packagingEnv, pkg, outdir, msiOutputDir);
|
||||
return msiPackager.andThen(exePackager);
|
||||
}).execute(WinPackagingPipeline.build());
|
||||
Path msiDir = env.buildRoot().resolve("msi");
|
||||
toRunnable(() -> Files.createDirectories(msiDir)).run();
|
||||
|
||||
// Write msi to temporary directory.
|
||||
Path msi = msiBundler.execute(params, msiDir);
|
||||
|
||||
try {
|
||||
new ScriptRunner()
|
||||
.setDirectory(msi.getParent())
|
||||
.setResourceCategoryId("resource.post-msi-script")
|
||||
.setScriptNameSuffix("post-msi")
|
||||
.setEnvironmentVariable("JpMsiFile", msi.toAbsolutePath().toString())
|
||||
.run(env, pkg.packageName());
|
||||
|
||||
var exePkg = new WinExePackageBuilder(pkg).icon(ICON.fetchFrom(params)).create();
|
||||
return buildEXE(env, exePkg, msi, outdir);
|
||||
} catch (IOException|ConfigException ex) {
|
||||
Log.verbose(ex);
|
||||
throw new PackagerException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static native int embedMSI(long resourceLock, String msiPath);
|
||||
private Path buildEXE(BuildEnv env, WinExePackage pkg, Path msi,
|
||||
Path outdir) throws IOException {
|
||||
|
||||
Log.verbose(I18N.format("message.outputting-to-location", outdir.toAbsolutePath()));
|
||||
|
||||
// Copy template msi wrapper next to msi file
|
||||
final Path exePath = msi.getParent().resolve(pkg.packageFileNameWithSuffix());
|
||||
|
||||
env.createResource("msiwrapper.exe")
|
||||
.setCategory(I18N.getString("resource.installer-exe"))
|
||||
.setPublicName("installer.exe")
|
||||
.saveToFile(exePath);
|
||||
|
||||
new ExecutableRebrander(pkg, env::createResource, resourceLock -> {
|
||||
// Embed msi in msi wrapper exe.
|
||||
embedMSI(resourceLock, msi.toAbsolutePath().toString());
|
||||
}).execute(env, exePath, pkg.icon());
|
||||
|
||||
Path dstExePath = outdir.resolve(exePath.getFileName());
|
||||
|
||||
Files.copy(exePath, dstExePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
dstExePath.toFile().setExecutable(true);
|
||||
|
||||
Log.verbose(I18N.format("message.output-location", outdir.toAbsolutePath()));
|
||||
|
||||
return dstExePath;
|
||||
}
|
||||
|
||||
private final WinMsiBundler msiBundler = new WinMsiBundler();
|
||||
|
||||
private static native int embedMSI(long resourceLock, String msiPath);
|
||||
}
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PackageTaskID;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PrimaryTaskID;
|
||||
import jdk.jpackage.internal.PackagingPipeline.TaskID;
|
||||
import jdk.jpackage.internal.model.WinExePackage;
|
||||
|
||||
final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Path msiOutputDir) implements Consumer<PackagingPipeline.Builder> {
|
||||
|
||||
WinExePackager {
|
||||
Objects.requireNonNull(env);
|
||||
Objects.requireNonNull(pkg);
|
||||
Objects.requireNonNull(outputDir);
|
||||
Objects.requireNonNull(msiOutputDir);
|
||||
}
|
||||
|
||||
enum ExePackageTaskID implements TaskID {
|
||||
RUN_POST_MSI_USER_SCRIPT,
|
||||
WRAP_MSI_IN_EXE
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(PackagingPipeline.Builder pipelineBuilder) {
|
||||
pipelineBuilder.excludeDirFromCopying(outputDir)
|
||||
.task(ExePackageTaskID.RUN_POST_MSI_USER_SCRIPT)
|
||||
.action(this::runPostMsiScript)
|
||||
.addDependency(PackageTaskID.CREATE_PACKAGE_FILE)
|
||||
.add()
|
||||
.task(ExePackageTaskID.WRAP_MSI_IN_EXE)
|
||||
.action(this::wrapMsiInExe)
|
||||
.addDependency(ExePackageTaskID.RUN_POST_MSI_USER_SCRIPT)
|
||||
.addDependent(PrimaryTaskID.PACKAGE)
|
||||
.add();
|
||||
}
|
||||
|
||||
private Path msi() {
|
||||
return msiOutputDir.resolve(pkg.msiPackage().packageFileNameWithSuffix());
|
||||
}
|
||||
|
||||
private void runPostMsiScript() throws IOException {
|
||||
new ScriptRunner()
|
||||
.setDirectory(msiOutputDir)
|
||||
.setResourceCategoryId("resource.post-msi-script")
|
||||
.setScriptNameSuffix("post-msi")
|
||||
.setEnvironmentVariable("JpMsiFile", msi().toAbsolutePath().toString())
|
||||
.run(env, pkg.msiPackage().packageName());
|
||||
}
|
||||
|
||||
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
|
||||
final Path exePath = msi.getParent().resolve(pkg.packageFileNameWithSuffix());
|
||||
|
||||
env.createResource("msiwrapper.exe")
|
||||
.setCategory(I18N.getString("resource.installer-exe"))
|
||||
.setPublicName("installer.exe")
|
||||
.saveToFile(exePath);
|
||||
|
||||
new ExecutableRebrander(pkg, env::createResource, resourceLock -> {
|
||||
// Embed msi in msi wrapper exe.
|
||||
WinExeBundler.embedMSI(resourceLock, msi.toAbsolutePath().toString());
|
||||
}).execute(env, exePath, pkg.icon());
|
||||
|
||||
Path dstExePath = outputDir.resolve(exePath.getFileName());
|
||||
|
||||
Files.createDirectories(dstExePath.getParent());
|
||||
Files.copy(exePath, dstExePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
dstExePath.toFile().setExecutable(true);
|
||||
|
||||
Log.verbose(I18N.format("message.output-location", outputDir.toAbsolutePath()));
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ import static jdk.jpackage.internal.FromParams.createApplicationBundlerParam;
|
||||
import static jdk.jpackage.internal.FromParams.createPackageBuilder;
|
||||
import static jdk.jpackage.internal.FromParams.createPackageBundlerParam;
|
||||
import static jdk.jpackage.internal.FromParams.findLauncherShortcut;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.ICON;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.RESOURCE_DIR;
|
||||
import static jdk.jpackage.internal.WinPackagingPipeline.APPLICATION_LAYOUT;
|
||||
import static jdk.jpackage.internal.model.StandardPackageType.WIN_MSI;
|
||||
@@ -42,7 +41,6 @@ import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.WinApplication;
|
||||
import jdk.jpackage.internal.model.WinExePackage;
|
||||
import jdk.jpackage.internal.model.WinLauncher;
|
||||
import jdk.jpackage.internal.model.WinLauncherMixin;
|
||||
import jdk.jpackage.internal.model.WinMsiPackage;
|
||||
@@ -101,26 +99,12 @@ final class WinFromParams {
|
||||
return pkgBuilder.create();
|
||||
}
|
||||
|
||||
private static WinExePackage createWinExePackage(Map<String, ? super Object> params) throws ConfigException, IOException {
|
||||
|
||||
final var msiPkg = MSI_PACKAGE.fetchFrom(params);
|
||||
|
||||
final var pkgBuilder = new WinExePackageBuilder(msiPkg);
|
||||
|
||||
ICON.copyInto(params, pkgBuilder::icon);
|
||||
|
||||
return pkgBuilder.create();
|
||||
}
|
||||
|
||||
static final BundlerParamInfo<WinApplication> APPLICATION = createApplicationBundlerParam(
|
||||
WinFromParams::createWinApplication);
|
||||
|
||||
static final BundlerParamInfo<WinMsiPackage> MSI_PACKAGE = createPackageBundlerParam(
|
||||
WinFromParams::createWinMsiPackage);
|
||||
|
||||
static final BundlerParamInfo<WinExePackage> EXE_PACKAGE = createPackageBundlerParam(
|
||||
WinFromParams::createWinExePackage);
|
||||
|
||||
private static final BundlerParamInfo<String> WIN_MENU_HINT = createStringBundlerParam(
|
||||
Arguments.CLIOptions.WIN_MENU_HINT.getId());
|
||||
|
||||
|
||||
@@ -27,16 +27,115 @@ package jdk.jpackage.internal;
|
||||
|
||||
import static jdk.jpackage.internal.model.ConfigException.rethrowConfigException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PackageBuildEnv;
|
||||
import jdk.jpackage.internal.model.AppImageLayout;
|
||||
import jdk.jpackage.internal.model.ApplicationLayout;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
import jdk.jpackage.internal.model.RuntimeLayout;
|
||||
import jdk.jpackage.internal.model.WinMsiPackage;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* WinMsiBundler
|
||||
*
|
||||
* Produces .msi installer from application image. Uses WiX Toolkit to build
|
||||
* .msi installer.
|
||||
* <p>
|
||||
* {@link #execute} method creates a number of source files with the description
|
||||
* of installer to be processed by WiX tools. Generated source files are stored
|
||||
* in "config" subdirectory next to "app" subdirectory in the root work
|
||||
* directory. The following WiX source files are generated:
|
||||
* <ul>
|
||||
* <li>main.wxs. Main source file with the installer description
|
||||
* <li>bundle.wxf. Source file with application and Java run-time directory tree
|
||||
* description.
|
||||
* <li>ui.wxf. Source file with UI description of the installer.
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* main.wxs file is a copy of main.wxs resource from
|
||||
* jdk.jpackage.internal.resources package. It is parametrized with the
|
||||
* following WiX variables:
|
||||
* <ul>
|
||||
* <li>JpAppName. Name of the application. Set to the value of --name command
|
||||
* line option
|
||||
* <li>JpAppVersion. Version of the application. Set to the value of
|
||||
* --app-version command line option
|
||||
* <li>JpAppVendor. Vendor of the application. Set to the value of --vendor
|
||||
* command line option
|
||||
* <li>JpAppDescription. Description of the application. Set to the value of
|
||||
* --description command line option
|
||||
* <li>JpProductCode. Set to product code UUID of the application. Random value
|
||||
* generated by jpackage every time {@link #execute} method is called
|
||||
* <li>JpProductUpgradeCode. Set to upgrade code UUID of the application. Random
|
||||
* value generated by jpackage every time {@link #execute} method is called if
|
||||
* --win-upgrade-uuid command line option is not specified. Otherwise this
|
||||
* variable is set to the value of --win-upgrade-uuid command line option
|
||||
* <li>JpAllowUpgrades. Set to "yes", but all that matters is it is defined.
|
||||
* <li>JpAllowDowngrades. Defined for application installers, and undefined for
|
||||
* Java runtime installers.
|
||||
* <li>JpConfigDir. Absolute path to the directory with generated WiX source
|
||||
* files.
|
||||
* <li>JpIsSystemWide. Set to "yes" if --win-per-user-install command line
|
||||
* option was not specified. Undefined otherwise
|
||||
* <li>JpAppSizeKb. Set to estimated size of the application in kilobytes
|
||||
* <li>JpHelpURL. Set to value of --win-help-url command line option if it
|
||||
* was specified. Undefined otherwise
|
||||
* <li>JpAboutURL. Set to value of --about-url command line option if it
|
||||
* was specified. Undefined otherwise
|
||||
* <li>JpUpdateURL. Set to value of --win-update-url command line option if it
|
||||
* was specified. Undefined otherwise
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* ui.wxf file is generated based on --license-file, --win-shortcut-prompt,
|
||||
* --win-dir-chooser command line options. It is parametrized with the following
|
||||
* WiX variables:
|
||||
* <ul>
|
||||
* <li>JpLicenseRtf. Set to the value of --license-file command line option.
|
||||
* Undefined if --license-file command line option was not specified
|
||||
* </ul>
|
||||
*/
|
||||
public class WinMsiBundler extends AbstractBundler {
|
||||
|
||||
public WinMsiBundler() {
|
||||
wixFragments = Stream.of(
|
||||
Map.entry("bundle.wxf", new WixAppImageFragmentBuilder()),
|
||||
Map.entry("ui.wxf", new WixUiFragmentBuilder()),
|
||||
Map.entry("os-condition.wxf", OSVersionCondition.createWixFragmentBuilder())
|
||||
).<WixFragmentBuilder>map(e -> {
|
||||
e.getValue().setOutputFileName(e.getKey());
|
||||
return e.getValue();
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,12 +156,10 @@ public class WinMsiBundler extends AbstractBundler {
|
||||
@Override
|
||||
public boolean supported(boolean platformInstaller) {
|
||||
try {
|
||||
try {
|
||||
sysEnv.orElseThrow();
|
||||
return true;
|
||||
} catch (RuntimeException ex) {
|
||||
ConfigException.rethrowConfigException(ex);
|
||||
if (wixToolset == null) {
|
||||
wixToolset = WixTool.createToolset();
|
||||
}
|
||||
return true;
|
||||
} catch (ConfigException ce) {
|
||||
Log.error(ce.getMessage());
|
||||
if (ce.getAdvice() != null) {
|
||||
@@ -87,7 +184,9 @@ public class WinMsiBundler extends AbstractBundler {
|
||||
WinFromParams.APPLICATION.fetchFrom(params);
|
||||
BuildEnvFromParams.BUILD_ENV.fetchFrom(params);
|
||||
|
||||
final var wixToolset = sysEnv.orElseThrow().wixToolset();
|
||||
if (wixToolset == null) {
|
||||
wixToolset = WixTool.createToolset();
|
||||
}
|
||||
|
||||
for (var tool : wixToolset.getType().getTools()) {
|
||||
Log.verbose(I18N.format("message.tool-version",
|
||||
@@ -95,23 +194,367 @@ public class WinMsiBundler extends AbstractBundler {
|
||||
wixToolset.getVersion()));
|
||||
}
|
||||
|
||||
wixFragments.forEach(wixFragment -> wixFragment.setWixVersion(wixToolset.getVersion(),
|
||||
wixToolset.getType()));
|
||||
|
||||
wixFragments.stream().map(WixFragmentBuilder::getLoggableWixFeatures).flatMap(
|
||||
List::stream).distinct().toList().forEach(Log::verbose);
|
||||
|
||||
return true;
|
||||
} catch (RuntimeException re) {
|
||||
throw rethrowConfigException(re);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareProto(Package pkg, BuildEnv env, AppImageLayout appImageLayout) throws
|
||||
PackagerException, IOException {
|
||||
|
||||
// Configure installer icon
|
||||
if (appImageLayout instanceof RuntimeLayout runtimeLayout) {
|
||||
// Use icon from java launcher.
|
||||
// Assume java.exe exists in Java Runtime being packed.
|
||||
// Ignore custom icon if any as we don't want to copy anything in
|
||||
// Java Runtime image.
|
||||
installerIcon = runtimeLayout.runtimeDirectory().resolve(Path.of("bin", "java.exe"));
|
||||
} else if (appImageLayout instanceof ApplicationLayout appLayout) {
|
||||
installerIcon = appLayout.launchersDirectory().resolve(
|
||||
pkg.app().mainLauncher().orElseThrow().executableNameWithSuffix());
|
||||
}
|
||||
installerIcon = installerIcon.toAbsolutePath();
|
||||
|
||||
pkg.licenseFile().ifPresent(licenseFile -> {
|
||||
// need to copy license file to the working directory
|
||||
// and convert to rtf if needed
|
||||
Path destFile = env.configDir().resolve(licenseFile.getFileName());
|
||||
|
||||
try {
|
||||
IOUtils.copyFile(licenseFile, destFile);
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
destFile.toFile().setWritable(true);
|
||||
ensureByMutationFileIsRTF(destFile);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path execute(Map<String, ? super Object> params,
|
||||
Path outputParentDir) throws PackagerException {
|
||||
|
||||
return Packager.<WinMsiPackage>build().outputDir(outputParentDir)
|
||||
.pkg(WinFromParams.MSI_PACKAGE.fetchFrom(params))
|
||||
.env(BuildEnvFromParams.BUILD_ENV.fetchFrom(params))
|
||||
.pipelineBuilderMutatorFactory((env, pkg, outputDir) -> {
|
||||
return new WinMsiPackager(env, pkg, outputDir, sysEnv.orElseThrow());
|
||||
}).execute(WinPackagingPipeline.build());
|
||||
IOUtils.writableOutputDir(outputParentDir);
|
||||
|
||||
// Order is important!
|
||||
var pkg = WinFromParams.MSI_PACKAGE.fetchFrom(params);
|
||||
var env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params);
|
||||
|
||||
WinPackagingPipeline.build()
|
||||
.excludeDirFromCopying(outputParentDir)
|
||||
.task(PackagingPipeline.PackageTaskID.CREATE_CONFIG_FILES)
|
||||
.packageAction(this::prepareConfigFiles)
|
||||
.add()
|
||||
.task(PackagingPipeline.PackageTaskID.CREATE_PACKAGE_FILE)
|
||||
.packageAction(this::buildPackage)
|
||||
.add()
|
||||
.create().execute(env, pkg, outputParentDir);
|
||||
|
||||
return outputParentDir.resolve(pkg.packageFileNameWithSuffix()).toAbsolutePath();
|
||||
}
|
||||
|
||||
final Result<WinSystemEnvironment> sysEnv = WinSystemEnvironment.create();
|
||||
private void prepareConfigFiles(PackageBuildEnv<WinMsiPackage, AppImageLayout> env) throws PackagerException, IOException {
|
||||
prepareProto(env.pkg(), env.env(), env.resolvedLayout());
|
||||
for (var wixFragment : wixFragments) {
|
||||
wixFragment.initFromParams(env.env(), env.pkg());
|
||||
wixFragment.addFilesToConfigRoot();
|
||||
}
|
||||
|
||||
final var msiOut = env.outputDir().resolve(env.pkg().packageFileNameWithSuffix());
|
||||
|
||||
Log.verbose(I18N.format("message.preparing-msi-config", msiOut.toAbsolutePath()));
|
||||
|
||||
final var wixVars = createWixVars(env);
|
||||
|
||||
final var wixObjDir = env.env().buildRoot().resolve("wixobj");
|
||||
|
||||
final var configDir = env.env().configDir();
|
||||
|
||||
final var wixPipelineBuilder = WixPipeline.build()
|
||||
.setWixObjDir(wixObjDir)
|
||||
.setWorkDir(env.env().appImageDir())
|
||||
.addSource(configDir.resolve("main.wxs"), wixVars);
|
||||
|
||||
for (var wixFragment : wixFragments) {
|
||||
wixFragment.configureWixPipeline(wixPipelineBuilder);
|
||||
}
|
||||
|
||||
switch (wixToolset.getType()) {
|
||||
case Wix3 -> {
|
||||
wixPipelineBuilder.addLightOptions("-sice:ICE27");
|
||||
|
||||
if (!env.pkg().isSystemWideInstall()) {
|
||||
wixPipelineBuilder.addLightOptions("-sice:ICE91");
|
||||
}
|
||||
}
|
||||
case Wix4 -> {
|
||||
}
|
||||
default -> {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
var primaryWxlFiles = Stream.of("de", "en", "ja", "zh_CN").map(loc -> {
|
||||
return configDir.resolve("MsiInstallerStrings_" + loc + ".wxl");
|
||||
}).toList();
|
||||
|
||||
var wixResources = new WixSourceConverter.ResourceGroup(wixToolset.getType());
|
||||
|
||||
// Copy standard l10n files.
|
||||
for (var path : primaryWxlFiles) {
|
||||
var name = path.getFileName().toString();
|
||||
wixResources.addResource(env.env().createResource(name).setPublicName(name).setCategory(
|
||||
I18N.getString("resource.wxl-file")), path);
|
||||
}
|
||||
|
||||
wixResources.addResource(env.env().createResource("main.wxs").setPublicName("main.wxs").
|
||||
setCategory(I18N.getString("resource.main-wix-file")), configDir.resolve("main.wxs"));
|
||||
|
||||
wixResources.addResource(env.env().createResource("overrides.wxi").setPublicName(
|
||||
"overrides.wxi").setCategory(I18N.getString("resource.overrides-wix-file")),
|
||||
configDir.resolve("overrides.wxi"));
|
||||
|
||||
// Filter out custom l10n files that were already used to
|
||||
// override primary l10n files. Ignore case filename comparison,
|
||||
// both lists are expected to be short.
|
||||
List<Path> customWxlFiles = env.env().resourceDir()
|
||||
.map(WinMsiBundler::getWxlFilesFromDir)
|
||||
.orElseGet(Collections::emptyList)
|
||||
.stream()
|
||||
.filter(custom -> primaryWxlFiles.stream().noneMatch(primary ->
|
||||
primary.getFileName().toString().equalsIgnoreCase(
|
||||
custom.getFileName().toString())))
|
||||
.peek(custom -> Log.verbose(I18N.format(
|
||||
"message.using-custom-resource", String.format("[%s]",
|
||||
I18N.getString("resource.wxl-file")),
|
||||
custom.getFileName()))).toList();
|
||||
|
||||
// Copy custom l10n files.
|
||||
for (var path : customWxlFiles) {
|
||||
var name = path.getFileName().toString();
|
||||
wixResources.addResource(env.env().createResource(name).setPublicName(name).
|
||||
setSourceOrder(OverridableResource.Source.ResourceDir).setCategory(I18N.
|
||||
getString("resource.wxl-file")), configDir.resolve(name));
|
||||
}
|
||||
|
||||
// Save all WiX resources into config dir.
|
||||
wixResources.saveResources();
|
||||
|
||||
// All l10n files are supplied to WiX with "-loc", but only
|
||||
// Cultures from custom files and a single primary Culture are
|
||||
// included into "-cultures" list
|
||||
for (var wxl : primaryWxlFiles) {
|
||||
wixPipelineBuilder.addLightOptions("-loc", wxl.toString());
|
||||
}
|
||||
|
||||
List<String> cultures = new ArrayList<>();
|
||||
for (var wxl : customWxlFiles) {
|
||||
wxl = configDir.resolve(wxl.getFileName());
|
||||
wixPipelineBuilder.addLightOptions("-loc", wxl.toString());
|
||||
cultures.add(getCultureFromWxlFile(wxl));
|
||||
}
|
||||
|
||||
// Append a primary culture bases on runtime locale.
|
||||
final Path primaryWxlFile = configDir.resolve(
|
||||
I18N.getString("resource.wxl-file-name"));
|
||||
cultures.add(getCultureFromWxlFile(primaryWxlFile));
|
||||
|
||||
// Build ordered list of unique cultures.
|
||||
Set<String> uniqueCultures = new LinkedHashSet<>();
|
||||
uniqueCultures.addAll(cultures);
|
||||
switch (wixToolset.getType()) {
|
||||
case Wix3 -> {
|
||||
wixPipelineBuilder.addLightOptions(uniqueCultures.stream().collect(Collectors.joining(";",
|
||||
"-cultures:", "")));
|
||||
}
|
||||
case Wix4 -> {
|
||||
uniqueCultures.forEach(culture -> {
|
||||
wixPipelineBuilder.addLightOptions("-culture", culture);
|
||||
});
|
||||
}
|
||||
default -> {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
Files.createDirectories(wixObjDir);
|
||||
wixPipeline = wixPipelineBuilder.create(wixToolset);
|
||||
}
|
||||
|
||||
private void buildPackage(PackageBuildEnv<WinMsiPackage, AppImageLayout> env) throws PackagerException, IOException {
|
||||
final var msiOut = env.outputDir().resolve(env.pkg().packageFileNameWithSuffix());
|
||||
Log.verbose(I18N.format("message.generating-msi", msiOut.toAbsolutePath()));
|
||||
wixPipeline.buildMsi(msiOut.toAbsolutePath());
|
||||
}
|
||||
|
||||
private Map<String, String> createWixVars(PackageBuildEnv<WinMsiPackage, AppImageLayout> env) throws IOException {
|
||||
Map<String, String> data = new HashMap<>();
|
||||
|
||||
final var pkg = env.pkg();
|
||||
|
||||
data.put("JpProductCode", pkg.productCode().toString());
|
||||
data.put("JpProductUpgradeCode", pkg.upgradeCode().toString());
|
||||
|
||||
Log.verbose(I18N.format("message.product-code", pkg.productCode()));
|
||||
Log.verbose(I18N.format("message.upgrade-code", pkg.upgradeCode()));
|
||||
|
||||
data.put("JpAllowUpgrades", "yes");
|
||||
if (!pkg.isRuntimeInstaller()) {
|
||||
data.put("JpAllowDowngrades", "yes");
|
||||
}
|
||||
|
||||
data.put("JpAppName", pkg.packageName());
|
||||
data.put("JpAppDescription", pkg.description());
|
||||
data.put("JpAppVendor", pkg.app().vendor());
|
||||
data.put("JpAppVersion", pkg.version());
|
||||
if (Files.exists(installerIcon)) {
|
||||
data.put("JpIcon", installerIcon.toString());
|
||||
}
|
||||
|
||||
pkg.helpURL().ifPresent(value -> {
|
||||
data.put("JpHelpURL", value);
|
||||
});
|
||||
|
||||
pkg.updateURL().ifPresent(value -> {
|
||||
data.put("JpUpdateURL", value);
|
||||
});
|
||||
|
||||
pkg.aboutURL().ifPresent(value -> {
|
||||
data.put("JpAboutURL", value);
|
||||
});
|
||||
|
||||
data.put("JpAppSizeKb", Long.toString(AppImageLayout.toPathGroup(
|
||||
env.resolvedLayout()).sizeInBytes() >> 10));
|
||||
|
||||
data.put("JpConfigDir", env.env().configDir().toAbsolutePath().toString());
|
||||
|
||||
if (pkg.isSystemWideInstall()) {
|
||||
data.put("JpIsSystemWide", "yes");
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static List<Path> getWxlFilesFromDir(Path dir) {
|
||||
final String glob = "glob:**/*.wxl";
|
||||
final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(
|
||||
glob);
|
||||
|
||||
try (var walk = Files.walk(dir, 1)) {
|
||||
return walk
|
||||
.filter(Files::isReadable)
|
||||
.filter(pathMatcher::matches)
|
||||
.sorted((a, b) -> a.getFileName().toString().compareToIgnoreCase(b.getFileName().toString()))
|
||||
.toList();
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getCultureFromWxlFile(Path wxlPath) {
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setNamespaceAware(false);
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
|
||||
Document doc = builder.parse(wxlPath.toFile());
|
||||
|
||||
XPath xPath = XPathFactory.newInstance().newXPath();
|
||||
NodeList nodes = (NodeList) xPath.evaluate(
|
||||
"//WixLocalization/@Culture", doc, XPathConstants.NODESET);
|
||||
if (nodes.getLength() != 1) {
|
||||
throw new IOException(I18N.format(
|
||||
"error.extract-culture-from-wix-l10n-file",
|
||||
wxlPath.toAbsolutePath().normalize()));
|
||||
}
|
||||
|
||||
return nodes.item(0).getNodeValue();
|
||||
} catch (XPathExpressionException | ParserConfigurationException
|
||||
| SAXException ex) {
|
||||
throw new UncheckedIOException(new IOException(
|
||||
I18N.format("error.read-wix-l10n-file", wxlPath.toAbsolutePath().normalize()), ex));
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureByMutationFileIsRTF(Path f) {
|
||||
try {
|
||||
boolean existingLicenseIsRTF = false;
|
||||
|
||||
try (InputStream fin = Files.newInputStream(f)) {
|
||||
byte[] firstBits = new byte[7];
|
||||
|
||||
if (fin.read(firstBits) == firstBits.length) {
|
||||
String header = new String(firstBits);
|
||||
existingLicenseIsRTF = "{\\rtf1\\".equals(header);
|
||||
}
|
||||
}
|
||||
|
||||
if (!existingLicenseIsRTF) {
|
||||
List<String> oldLicense = Files.readAllLines(f);
|
||||
try (Writer w = Files.newBufferedWriter(
|
||||
f, Charset.forName("Windows-1252"))) {
|
||||
w.write("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033"
|
||||
+ "{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}}\n"
|
||||
+ "\\viewkind4\\uc1\\pard\\sa200\\sl276"
|
||||
+ "\\slmult1\\lang9\\fs20 ");
|
||||
oldLicense.forEach(l -> {
|
||||
try {
|
||||
for (char c : l.toCharArray()) {
|
||||
// 0x00 <= ch < 0x20 Escaped (\'hh)
|
||||
// 0x20 <= ch < 0x80 Raw(non - escaped) char
|
||||
// 0x80 <= ch <= 0xFF Escaped(\ 'hh)
|
||||
// 0x5C, 0x7B, 0x7D (special RTF characters
|
||||
// \,{,})Escaped(\'hh)
|
||||
// ch > 0xff Escaped (\\ud###?)
|
||||
if (c < 0x10) {
|
||||
w.write("\\'0");
|
||||
w.write(Integer.toHexString(c));
|
||||
} else if (c > 0xff) {
|
||||
w.write("\\ud");
|
||||
w.write(Integer.toString(c));
|
||||
// \\uc1 is in the header and in effect
|
||||
// so we trail with a replacement char if
|
||||
// the font lacks that character - '?'
|
||||
w.write("?");
|
||||
} else if ((c < 0x20) || (c >= 0x80) ||
|
||||
(c == 0x5C) || (c == 0x7B) ||
|
||||
(c == 0x7D)) {
|
||||
w.write("\\'");
|
||||
w.write(Integer.toHexString(c));
|
||||
} else {
|
||||
w.write(c);
|
||||
}
|
||||
}
|
||||
// blank lines are interpreted as paragraph breaks
|
||||
if (l.length() < 1) {
|
||||
w.write("\\par");
|
||||
} else {
|
||||
w.write(" ");
|
||||
}
|
||||
w.write("\r\n");
|
||||
} catch (IOException e) {
|
||||
Log.verbose(e);
|
||||
}
|
||||
});
|
||||
w.write("}\r\n");
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.verbose(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Path installerIcon;
|
||||
private WixToolset wixToolset;
|
||||
private WixPipeline wixPipeline;
|
||||
private final List<WixFragmentBuilder> wixFragments;
|
||||
}
|
||||
|
||||
@@ -1,486 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import jdk.jpackage.internal.model.AppImageLayout;
|
||||
import jdk.jpackage.internal.model.PackagerException;
|
||||
import jdk.jpackage.internal.model.RuntimeLayout;
|
||||
import jdk.jpackage.internal.model.WinMsiPackage;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* WinMsiPackager
|
||||
*
|
||||
* Produces .msi installer from application image. Uses WiX Toolkit to build
|
||||
* .msi installer.
|
||||
* <p>
|
||||
* Creates a number of source files with the description
|
||||
* of installer to be processed by WiX tools. Generated source files are stored
|
||||
* in "config" subdirectory next to "app" subdirectory in the root work
|
||||
* directory. The following WiX source files are generated:
|
||||
* <ul>
|
||||
* <li>main.wxs. Main source file with the installer description
|
||||
* <li>bundle.wxf. Source file with application and Java run-time directory tree
|
||||
* description.
|
||||
* <li>ui.wxf. Source file with UI description of the installer.
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* main.wxs file is a copy of main.wxs resource from
|
||||
* jdk.jpackage.internal.resources package. It is parametrized with the
|
||||
* following WiX variables:
|
||||
* <ul>
|
||||
* <li>JpAppName. Name of the application. Set to the value of --name command
|
||||
* line option
|
||||
* <li>JpAppVersion. Version of the application. Set to the value of
|
||||
* --app-version command line option
|
||||
* <li>JpAppVendor. Vendor of the application. Set to the value of --vendor
|
||||
* command line option
|
||||
* <li>JpAppDescription. Description of the application. Set to the value of
|
||||
* --description command line option
|
||||
* <li>JpProductCode. Set to product code UUID of the application. Random value
|
||||
* generated by jpackage every time {@link #execute} method is called
|
||||
* <li>JpProductUpgradeCode. Set to upgrade code UUID of the application. Random
|
||||
* value generated by jpackage every time {@link #execute} method is called if
|
||||
* --win-upgrade-uuid command line option is not specified. Otherwise this
|
||||
* variable is set to the value of --win-upgrade-uuid command line option
|
||||
* <li>JpAllowUpgrades. Set to "yes", but all that matters is it is defined.
|
||||
* <li>JpAllowDowngrades. Defined for application installers, and undefined for
|
||||
* Java runtime installers.
|
||||
* <li>JpConfigDir. Absolute path to the directory with generated WiX source
|
||||
* files.
|
||||
* <li>JpIsSystemWide. Set to "yes" if --win-per-user-install command line
|
||||
* option was not specified. Undefined otherwise
|
||||
* <li>JpAppSizeKb. Set to estimated size of the application in kilobytes
|
||||
* <li>JpHelpURL. Set to value of --win-help-url command line option if it
|
||||
* was specified. Undefined otherwise
|
||||
* <li>JpAboutURL. Set to value of --about-url command line option if it
|
||||
* was specified. Undefined otherwise
|
||||
* <li>JpUpdateURL. Set to value of --win-update-url command line option if it
|
||||
* was specified. Undefined otherwise
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* ui.wxf file is generated based on --license-file, --win-shortcut-prompt,
|
||||
* --win-dir-chooser command line options. It is parametrized with the following
|
||||
* WiX variables:
|
||||
* <ul>
|
||||
* <li>JpLicenseRtf. Set to the value of --license-file command line option.
|
||||
* Undefined if --license-file command line option was not specified
|
||||
* </ul>
|
||||
*/
|
||||
final class WinMsiPackager implements Consumer<PackagingPipeline.Builder> {
|
||||
|
||||
WinMsiPackager(BuildEnv env, WinMsiPackage pkg, Path outputDir, WixToolset wixToolset) {
|
||||
this.pkg = Objects.requireNonNull(pkg);
|
||||
this.env = Objects.requireNonNull(env);
|
||||
this.outputDir = Objects.requireNonNull(outputDir);
|
||||
this.wixToolset = Objects.requireNonNull(wixToolset);
|
||||
|
||||
wixFragments = Stream.of(
|
||||
Map.entry("bundle.wxf", new WixAppImageFragmentBuilder()),
|
||||
Map.entry("ui.wxf", new WixUiFragmentBuilder()),
|
||||
Map.entry("os-condition.wxf", OSVersionCondition.createWixFragmentBuilder())
|
||||
).<WixFragmentBuilder>map(e -> {
|
||||
e.getValue().setOutputFileName(e.getKey());
|
||||
return e.getValue();
|
||||
}).toList();
|
||||
|
||||
// Configure installer icon
|
||||
if (env.appImageLayout() instanceof RuntimeLayout runtimeLayout) {
|
||||
// Use icon from java launcher.
|
||||
// Assume java.exe exists in Java Runtime being packed.
|
||||
// Ignore custom icon if any as we don't want to copy anything in
|
||||
// Java Runtime image.
|
||||
installerIcon = runtimeLayout.runtimeDirectory().resolve(Path.of("bin", "java.exe")).toAbsolutePath();
|
||||
} else {
|
||||
installerIcon = env.asApplicationLayout().orElseThrow().launchersDirectory().resolve(
|
||||
pkg.app().mainLauncher().orElseThrow().executableNameWithSuffix()).toAbsolutePath();
|
||||
}
|
||||
|
||||
wixFragments.forEach(wixFragment -> wixFragment.setWixVersion(wixToolset.getVersion(),
|
||||
wixToolset.getType()));
|
||||
|
||||
wixFragments.stream().map(WixFragmentBuilder::getLoggableWixFeatures).flatMap(
|
||||
List::stream).distinct().toList().forEach(Log::verbose);
|
||||
}
|
||||
|
||||
WinMsiPackager(BuildEnv env, WinMsiPackage pkg, Path outputDir, WinSystemEnvironment sysEnv) {
|
||||
this(env, pkg, outputDir, sysEnv.wixToolset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(PackagingPipeline.Builder pipelineBuilder) {
|
||||
pipelineBuilder.excludeDirFromCopying(outputDir)
|
||||
.task(PackagingPipeline.PackageTaskID.CREATE_CONFIG_FILES)
|
||||
.action(this::prepareConfigFiles)
|
||||
.add()
|
||||
.task(PackagingPipeline.PackageTaskID.CREATE_PACKAGE_FILE)
|
||||
.action(this::buildPackage)
|
||||
.add();
|
||||
}
|
||||
|
||||
private void prepareConfigFiles() throws PackagerException, IOException {
|
||||
|
||||
pkg.licenseFile().ifPresent(licenseFile -> {
|
||||
// need to copy license file to the working directory
|
||||
// and convert to rtf if needed
|
||||
Path destFile = env.configDir().resolve(licenseFile.getFileName());
|
||||
|
||||
try {
|
||||
IOUtils.copyFile(licenseFile, destFile);
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
destFile.toFile().setWritable(true);
|
||||
ensureByMutationFileIsRTF(destFile);
|
||||
});
|
||||
|
||||
for (var wixFragment : wixFragments) {
|
||||
wixFragment.initFromParams(env, pkg);
|
||||
wixFragment.addFilesToConfigRoot();
|
||||
}
|
||||
|
||||
final var msiOut = outputDir.resolve(pkg.packageFileNameWithSuffix());
|
||||
|
||||
Log.verbose(I18N.format("message.preparing-msi-config", msiOut.toAbsolutePath()));
|
||||
|
||||
final var wixVars = createWixVars();
|
||||
|
||||
final var wixObjDir = env.buildRoot().resolve("wixobj");
|
||||
|
||||
final var configDir = env.configDir();
|
||||
|
||||
final var wixPipelineBuilder = WixPipeline.build()
|
||||
.setWixObjDir(wixObjDir)
|
||||
.setWorkDir(env.appImageDir())
|
||||
.addSource(configDir.resolve("main.wxs"), wixVars);
|
||||
|
||||
for (var wixFragment : wixFragments) {
|
||||
wixFragment.configureWixPipeline(wixPipelineBuilder);
|
||||
}
|
||||
|
||||
switch (wixToolset.getType()) {
|
||||
case Wix3 -> {
|
||||
wixPipelineBuilder.addLightOptions("-sice:ICE27");
|
||||
|
||||
if (!pkg.isSystemWideInstall()) {
|
||||
wixPipelineBuilder.addLightOptions("-sice:ICE91");
|
||||
}
|
||||
}
|
||||
case Wix4 -> {
|
||||
}
|
||||
default -> {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
var primaryWxlFiles = Stream.of("de", "en", "ja", "zh_CN").map(loc -> {
|
||||
return configDir.resolve("MsiInstallerStrings_" + loc + ".wxl");
|
||||
}).toList();
|
||||
|
||||
var wixResources = new WixSourceConverter.ResourceGroup(wixToolset.getType());
|
||||
|
||||
// Copy standard l10n files.
|
||||
for (var path : primaryWxlFiles) {
|
||||
var name = path.getFileName().toString();
|
||||
wixResources.addResource(env.createResource(name).setPublicName(name).setCategory(
|
||||
I18N.getString("resource.wxl-file")), path);
|
||||
}
|
||||
|
||||
wixResources.addResource(env.createResource("main.wxs").setPublicName("main.wxs").
|
||||
setCategory(I18N.getString("resource.main-wix-file")), configDir.resolve("main.wxs"));
|
||||
|
||||
wixResources.addResource(env.createResource("overrides.wxi").setPublicName(
|
||||
"overrides.wxi").setCategory(I18N.getString("resource.overrides-wix-file")),
|
||||
configDir.resolve("overrides.wxi"));
|
||||
|
||||
// Filter out custom l10n files that were already used to
|
||||
// override primary l10n files. Ignore case filename comparison,
|
||||
// both lists are expected to be short.
|
||||
List<Path> customWxlFiles = env.resourceDir()
|
||||
.map(WinMsiPackager::getWxlFilesFromDir)
|
||||
.orElseGet(Collections::emptyList)
|
||||
.stream()
|
||||
.filter(custom -> primaryWxlFiles.stream().noneMatch(primary ->
|
||||
primary.getFileName().toString().equalsIgnoreCase(
|
||||
custom.getFileName().toString())))
|
||||
.peek(custom -> Log.verbose(I18N.format(
|
||||
"message.using-custom-resource", String.format("[%s]",
|
||||
I18N.getString("resource.wxl-file")),
|
||||
custom.getFileName()))).toList();
|
||||
|
||||
// Copy custom l10n files.
|
||||
for (var path : customWxlFiles) {
|
||||
var name = path.getFileName().toString();
|
||||
wixResources.addResource(env.createResource(name).setPublicName(name).
|
||||
setSourceOrder(OverridableResource.Source.ResourceDir).setCategory(I18N.
|
||||
getString("resource.wxl-file")), configDir.resolve(name));
|
||||
}
|
||||
|
||||
// Save all WiX resources into config dir.
|
||||
wixResources.saveResources();
|
||||
|
||||
// All l10n files are supplied to WiX with "-loc", but only
|
||||
// Cultures from custom files and a single primary Culture are
|
||||
// included into "-cultures" list
|
||||
for (var wxl : primaryWxlFiles) {
|
||||
wixPipelineBuilder.addLightOptions("-loc", wxl.toString());
|
||||
}
|
||||
|
||||
List<String> cultures = new ArrayList<>();
|
||||
for (var wxl : customWxlFiles) {
|
||||
wxl = configDir.resolve(wxl.getFileName());
|
||||
wixPipelineBuilder.addLightOptions("-loc", wxl.toString());
|
||||
cultures.add(getCultureFromWxlFile(wxl));
|
||||
}
|
||||
|
||||
// Append a primary culture bases on runtime locale.
|
||||
final Path primaryWxlFile = configDir.resolve(
|
||||
I18N.getString("resource.wxl-file-name"));
|
||||
cultures.add(getCultureFromWxlFile(primaryWxlFile));
|
||||
|
||||
// Build ordered list of unique cultures.
|
||||
Set<String> uniqueCultures = new LinkedHashSet<>();
|
||||
uniqueCultures.addAll(cultures);
|
||||
switch (wixToolset.getType()) {
|
||||
case Wix3 -> {
|
||||
wixPipelineBuilder.addLightOptions(uniqueCultures.stream().collect(Collectors.joining(";",
|
||||
"-cultures:", "")));
|
||||
}
|
||||
case Wix4 -> {
|
||||
uniqueCultures.forEach(culture -> {
|
||||
wixPipelineBuilder.addLightOptions("-culture", culture);
|
||||
});
|
||||
}
|
||||
default -> {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
Files.createDirectories(wixObjDir);
|
||||
wixPipeline = wixPipelineBuilder.create(wixToolset);
|
||||
}
|
||||
|
||||
private void buildPackage() throws PackagerException, IOException {
|
||||
final var msiOut = outputDir.resolve(pkg.packageFileNameWithSuffix());
|
||||
Log.verbose(I18N.format("message.generating-msi", msiOut.toAbsolutePath()));
|
||||
wixPipeline.buildMsi(msiOut.toAbsolutePath());
|
||||
}
|
||||
|
||||
private Map<String, String> createWixVars() throws IOException {
|
||||
Map<String, String> data = new HashMap<>();
|
||||
|
||||
data.put("JpProductCode", pkg.productCode().toString());
|
||||
data.put("JpProductUpgradeCode", pkg.upgradeCode().toString());
|
||||
|
||||
Log.verbose(I18N.format("message.product-code", pkg.productCode()));
|
||||
Log.verbose(I18N.format("message.upgrade-code", pkg.upgradeCode()));
|
||||
|
||||
data.put("JpAllowUpgrades", "yes");
|
||||
if (!pkg.isRuntimeInstaller()) {
|
||||
data.put("JpAllowDowngrades", "yes");
|
||||
}
|
||||
|
||||
data.put("JpAppName", pkg.packageName());
|
||||
data.put("JpAppDescription", pkg.description());
|
||||
data.put("JpAppVendor", pkg.app().vendor());
|
||||
data.put("JpAppVersion", pkg.version());
|
||||
if (Files.exists(installerIcon)) {
|
||||
data.put("JpIcon", installerIcon.toString());
|
||||
}
|
||||
|
||||
pkg.helpURL().ifPresent(value -> {
|
||||
data.put("JpHelpURL", value);
|
||||
});
|
||||
|
||||
pkg.updateURL().ifPresent(value -> {
|
||||
data.put("JpUpdateURL", value);
|
||||
});
|
||||
|
||||
pkg.aboutURL().ifPresent(value -> {
|
||||
data.put("JpAboutURL", value);
|
||||
});
|
||||
|
||||
data.put("JpAppSizeKb", Long.toString(AppImageLayout.toPathGroup(
|
||||
env.appImageLayout()).sizeInBytes() >> 10));
|
||||
|
||||
data.put("JpConfigDir", env.configDir().toAbsolutePath().toString());
|
||||
|
||||
if (pkg.isSystemWideInstall()) {
|
||||
data.put("JpIsSystemWide", "yes");
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static List<Path> getWxlFilesFromDir(Path dir) {
|
||||
final String glob = "glob:**/*.wxl";
|
||||
final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(
|
||||
glob);
|
||||
|
||||
try (var walk = Files.walk(dir, 1)) {
|
||||
return walk
|
||||
.filter(Files::isReadable)
|
||||
.filter(pathMatcher::matches)
|
||||
.sorted((a, b) -> a.getFileName().toString().compareToIgnoreCase(b.getFileName().toString()))
|
||||
.toList();
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getCultureFromWxlFile(Path wxlPath) {
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setNamespaceAware(false);
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
|
||||
Document doc = builder.parse(wxlPath.toFile());
|
||||
|
||||
XPath xPath = XPathFactory.newInstance().newXPath();
|
||||
NodeList nodes = (NodeList) xPath.evaluate(
|
||||
"//WixLocalization/@Culture", doc, XPathConstants.NODESET);
|
||||
if (nodes.getLength() != 1) {
|
||||
throw new RuntimeException(I18N.format(
|
||||
"error.extract-culture-from-wix-l10n-file",
|
||||
wxlPath.toAbsolutePath().normalize()));
|
||||
}
|
||||
|
||||
return nodes.item(0).getNodeValue();
|
||||
} catch (XPathExpressionException | ParserConfigurationException | SAXException ex) {
|
||||
throw new RuntimeException(I18N.format(
|
||||
"error.read-wix-l10n-file", wxlPath.toAbsolutePath().normalize()), ex);
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureByMutationFileIsRTF(Path f) {
|
||||
try {
|
||||
boolean existingLicenseIsRTF = false;
|
||||
|
||||
try (InputStream fin = Files.newInputStream(f)) {
|
||||
byte[] firstBits = new byte[7];
|
||||
|
||||
if (fin.read(firstBits) == firstBits.length) {
|
||||
String header = new String(firstBits);
|
||||
existingLicenseIsRTF = "{\\rtf1\\".equals(header);
|
||||
}
|
||||
}
|
||||
|
||||
if (!existingLicenseIsRTF) {
|
||||
List<String> oldLicense = Files.readAllLines(f);
|
||||
try (Writer w = Files.newBufferedWriter(
|
||||
f, Charset.forName("Windows-1252"))) {
|
||||
w.write("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033"
|
||||
+ "{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}}\n"
|
||||
+ "\\viewkind4\\uc1\\pard\\sa200\\sl276"
|
||||
+ "\\slmult1\\lang9\\fs20 ");
|
||||
oldLicense.forEach(l -> {
|
||||
try {
|
||||
for (char c : l.toCharArray()) {
|
||||
// 0x00 <= ch < 0x20 Escaped (\'hh)
|
||||
// 0x20 <= ch < 0x80 Raw(non - escaped) char
|
||||
// 0x80 <= ch <= 0xFF Escaped(\ 'hh)
|
||||
// 0x5C, 0x7B, 0x7D (special RTF characters
|
||||
// \,{,})Escaped(\'hh)
|
||||
// ch > 0xff Escaped (\\ud###?)
|
||||
if (c < 0x10) {
|
||||
w.write("\\'0");
|
||||
w.write(Integer.toHexString(c));
|
||||
} else if (c > 0xff) {
|
||||
w.write("\\ud");
|
||||
w.write(Integer.toString(c));
|
||||
// \\uc1 is in the header and in effect
|
||||
// so we trail with a replacement char if
|
||||
// the font lacks that character - '?'
|
||||
w.write("?");
|
||||
} else if ((c < 0x20) || (c >= 0x80) ||
|
||||
(c == 0x5C) || (c == 0x7B) ||
|
||||
(c == 0x7D)) {
|
||||
w.write("\\'");
|
||||
w.write(Integer.toHexString(c));
|
||||
} else {
|
||||
w.write(c);
|
||||
}
|
||||
}
|
||||
// blank lines are interpreted as paragraph breaks
|
||||
if (l.length() < 1) {
|
||||
w.write("\\par");
|
||||
} else {
|
||||
w.write(" ");
|
||||
}
|
||||
w.write("\r\n");
|
||||
} catch (IOException e) {
|
||||
Log.verbose(e);
|
||||
}
|
||||
});
|
||||
w.write("}\r\n");
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.verbose(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final WinMsiPackage pkg;
|
||||
private final BuildEnv env;
|
||||
private final Path outputDir;
|
||||
private final WixToolset wixToolset;
|
||||
private final List<WixFragmentBuilder> wixFragments;
|
||||
private final Path installerIcon;
|
||||
private WixPipeline wixPipeline;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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;
|
||||
|
||||
import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier;
|
||||
|
||||
import java.util.Objects;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
record WinSystemEnvironment(WixToolset wixToolset) implements SystemEnvironment {
|
||||
|
||||
WinSystemEnvironment {
|
||||
Objects.requireNonNull(wixToolset);
|
||||
}
|
||||
|
||||
static Result<WinSystemEnvironment> create() {
|
||||
return Result.create(toSupplier(WixTool::createToolset)).map(WinSystemEnvironment::new);
|
||||
}
|
||||
}
|
||||
@@ -183,12 +183,13 @@ public enum WixTool {
|
||||
final boolean[] tooOld = new boolean[1];
|
||||
final String[] parsedVersion = new String[1];
|
||||
|
||||
final var validator = new ToolValidator(toolPath)
|
||||
.setMinimalVersion(tool.minimalVersion)
|
||||
.setToolOldVersionErrorHandler((name, version) -> {
|
||||
tooOld[0] = true;
|
||||
return null;
|
||||
});
|
||||
final var validator = new ToolValidator(toolPath).setMinimalVersion(tool.minimalVersion).
|
||||
setToolNotFoundErrorHandler((name, ex) -> {
|
||||
return new ConfigException("", "");
|
||||
}).setToolOldVersionErrorHandler((name, version) -> {
|
||||
tooOld[0] = true;
|
||||
return null;
|
||||
});
|
||||
|
||||
final Function<Stream<String>, String> versionParser;
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
#include "unittest.hpp"
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
static bool using_explicit_hugepages() { return UseLargePages && !UseTransparentHugePages; }
|
||||
|
||||
@@ -469,27 +468,4 @@ TEST_VM(os_linux, glibc_mallinfo_wrapper) {
|
||||
#endif // ADDRESS_SANITIZER
|
||||
#endif // __GLIBC__
|
||||
|
||||
static void test_set_thread_name(const char* name, const char* expected) {
|
||||
os::set_native_thread_name(name);
|
||||
char buf[16];
|
||||
int rc = prctl(PR_GET_NAME, buf);
|
||||
ASSERT_EQ(0, rc);
|
||||
ASSERT_STREQ(buf, expected);
|
||||
}
|
||||
|
||||
TEST_VM(os_linux, set_thread_name) {
|
||||
char buf[16];
|
||||
// retrieve current name
|
||||
int rc = prctl(PR_GET_NAME, buf);
|
||||
ASSERT_EQ(0, rc);
|
||||
|
||||
test_set_thread_name("shortname", "shortname");
|
||||
test_set_thread_name("012345678901234", "012345678901234");
|
||||
test_set_thread_name("0123456789012345", "0123456..012345");
|
||||
test_set_thread_name("MyAllocationWorkerThread22", "MyAlloc..read22");
|
||||
|
||||
// restore current name
|
||||
test_set_thread_name(buf, buf);
|
||||
}
|
||||
|
||||
#endif // LINUX
|
||||
|
||||
@@ -65,7 +65,6 @@ public class OldClassSupport {
|
||||
@Override
|
||||
public String[] vmArgs(RunMode runMode) {
|
||||
return new String[] {
|
||||
"-Xlog:aot",
|
||||
"-Xlog:aot+class=debug",
|
||||
"-Xlog:aot+resolve=trace",
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ import jdk.test.lib.process.OutputAnalyzer;
|
||||
public class VerifierFailOver {
|
||||
public static void main(String... args) throws Exception {
|
||||
SimpleCDSAppTester.of("VerifierFailOver")
|
||||
.addVmArgs("-Xlog:aot,aot+class=debug")
|
||||
.addVmArgs("-Xlog:aot+class=debug")
|
||||
.classpath("app.jar")
|
||||
.appCommandLine("VerifierFailOverApp")
|
||||
.setTrainingChecker((OutputAnalyzer out) -> {
|
||||
|
||||
@@ -118,7 +118,7 @@ public class BulkLoaderTest {
|
||||
@Override
|
||||
public String[] vmArgs(RunMode runMode) {
|
||||
return new String[] {
|
||||
"-Xlog:cds,aot,aot+load,cds+class=debug,aot+class=debug",
|
||||
"-Xlog:cds,aot+load,cds+class=debug,aot+class=debug",
|
||||
"-XX:+AOTClassLinking",
|
||||
};
|
||||
}
|
||||
|
||||
+21
-26
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2021, 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,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8153029 8360463
|
||||
* @bug 8153029
|
||||
* @library /test/lib
|
||||
* @run main ChaCha20CipherUnitTest
|
||||
* @summary Unit test for com.sun.crypto.provider.ChaCha20Cipher.
|
||||
@@ -38,7 +38,6 @@ import java.util.Arrays;
|
||||
import java.util.HexFormat;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.ChaCha20ParameterSpec;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
@@ -67,36 +66,32 @@ public class ChaCha20CipherUnitTest {
|
||||
private static void testTransformations() throws Exception {
|
||||
System.out.println("== transformations ==");
|
||||
|
||||
Class NSAE = NoSuchAlgorithmException.class;
|
||||
Class NSPE = NoSuchPaddingException.class;
|
||||
checkTransformation("ChaCha20", true);
|
||||
checkTransformation("ChaCha20/None/NoPadding", true);
|
||||
checkTransformation("ChaCha20-Poly1305", true);
|
||||
checkTransformation("ChaCha20-Poly1305/None/NoPadding", true);
|
||||
|
||||
checkTransformation("ChaCha20", null);
|
||||
checkTransformation("ChaCha20/None/NoPadding", null);
|
||||
checkTransformation("ChaCha20-Poly1305", null);
|
||||
checkTransformation("ChaCha20-Poly1305/None/NoPadding", null);
|
||||
checkTransformation("ChaCha20/ECB/NoPadding", NSAE);
|
||||
checkTransformation("ChaCha20/None/PKCS5Padding", NSPE);
|
||||
checkTransformation("ChaCha20-Poly1305/ECB/NoPadding", NSAE);
|
||||
checkTransformation("ChaCha20-Poly1305/None/PKCS5Padding", NSPE);
|
||||
checkTransformation("ChaCha20/ECB/NoPadding", false);
|
||||
checkTransformation("ChaCha20/None/PKCS5Padding", false);
|
||||
checkTransformation("ChaCha20-Poly1305/ECB/NoPadding", false);
|
||||
checkTransformation("ChaCha20-Poly1305/None/PKCS5Padding", false);
|
||||
}
|
||||
|
||||
private static void checkTransformation(String transformation, Class exCls)
|
||||
throws Exception {
|
||||
private static void checkTransformation(String transformation,
|
||||
boolean expected) throws Exception {
|
||||
try {
|
||||
Cipher.getInstance(transformation,
|
||||
System.getProperty("test.provider.name", "SunJCE"));
|
||||
if (exCls != null) {
|
||||
throw new RuntimeException("Expected Exception not thrown: " +
|
||||
exCls);
|
||||
Cipher.getInstance(transformation);
|
||||
if (!expected) {
|
||||
throw new RuntimeException(
|
||||
"Unexpected transformation: " + transformation);
|
||||
} else {
|
||||
System.out.println(transformation + ": pass");
|
||||
System.out.println("Expected transformation: " + transformation);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
||||
if (e.getClass() != exCls) {
|
||||
throw new RuntimeException("Unexpected Exception", e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
if (!expected) {
|
||||
System.out.println("Unexpected transformation: " + transformation);
|
||||
} else {
|
||||
System.out.println(transformation + ": got expected " +
|
||||
exCls.getName());
|
||||
throw new RuntimeException("Unexpected fail: " + transformation, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8354522 8358880 8367324
|
||||
* @bug 8354522 8358880
|
||||
* @summary Check for cloning interference
|
||||
* @library /test/lib
|
||||
* @run junit/othervm --add-opens=java.base/java.text=ALL-UNNAMED CloneTest
|
||||
@@ -89,6 +89,12 @@ public class CloneTest {
|
||||
Object digits = valFromDigitList(original, "digits");
|
||||
assertNotSame(digits, valFromDigitList(dfClone, "digits"));
|
||||
|
||||
|
||||
Object data = valFromDigitList(original, "data");
|
||||
if (data != null) {
|
||||
assertNotSame(data, valFromDigitList(dfClone, "data"));
|
||||
}
|
||||
|
||||
assertEquals(digitListField.get(original), digitListField.get(dfClone));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new SkippedException("reflective access in white-box test failed", e);
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// SunJSSE does not support dynamic system properties, no way to re-use
|
||||
// system properties in samevm/agentvm mode.
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8367133
|
||||
* @summary Verify that handshake succeeds when Finished message is fragmented
|
||||
* @modules java.base/sun.security.util
|
||||
* @library /test/lib
|
||||
* @build DTLSOverDatagram
|
||||
* @run main/othervm FragmentedFinished
|
||||
*/
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FragmentedFinished extends DTLSOverDatagram {
|
||||
private SSLEngine serverSSLEngine;
|
||||
public static void main(String[] args) throws Exception {
|
||||
FragmentedFinished testCase = new FragmentedFinished();
|
||||
testCase.runTest(testCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
SSLEngine createSSLEngine(boolean isClient) throws Exception {
|
||||
SSLEngine sslEngine = super.createSSLEngine(isClient);
|
||||
if (!isClient) {
|
||||
serverSSLEngine = sslEngine;
|
||||
}
|
||||
return sslEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
|
||||
if (ba.length < 30) { // detect ChangeCipherSpec
|
||||
// Reduce the maximumPacketSize to force fragmentation
|
||||
// of the Finished message
|
||||
SSLParameters params = serverSSLEngine.getSSLParameters();
|
||||
params.setMaximumPacketSize(53);
|
||||
serverSSLEngine.setSSLParameters(params);
|
||||
}
|
||||
|
||||
return super.createHandshakePacket(ba, socketAddr);
|
||||
}
|
||||
}
|
||||
@@ -611,7 +611,7 @@ public class Byte128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Byte128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
ByteVector av = ByteVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Byte256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Byte256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
ByteVector av = ByteVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Byte512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Byte512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
ByteVector av = ByteVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Byte64VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Byte64VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
ByteVector av = ByteVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -618,7 +618,7 @@ public class ByteMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -649,7 +649,7 @@ public class ByteMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
ByteVector av = ByteVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Double128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Double128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
DoubleVector av = DoubleVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Double256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Double256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
DoubleVector av = DoubleVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Double512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Double512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
DoubleVector av = DoubleVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Double64VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Double64VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
DoubleVector av = DoubleVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -618,7 +618,7 @@ public class DoubleMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -649,7 +649,7 @@ public class DoubleMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
DoubleVector av = DoubleVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Float128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Float128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
FloatVector av = FloatVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Float256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Float256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
FloatVector av = FloatVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Float512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Float512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
FloatVector av = FloatVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Float64VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Float64VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
FloatVector av = FloatVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -618,7 +618,7 @@ public class FloatMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -649,7 +649,7 @@ public class FloatMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
FloatVector av = FloatVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Int128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Int128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
IntVector av = IntVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Int256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Int256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
IntVector av = IntVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Int512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Int512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
IntVector av = IntVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Int64VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Int64VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
IntVector av = IntVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -618,7 +618,7 @@ public class IntMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -649,7 +649,7 @@ public class IntMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
IntVector av = IntVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Long128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Long128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
LongVector av = LongVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Long256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Long256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
LongVector av = LongVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Long512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Long512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
LongVector av = LongVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Long64VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Long64VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
LongVector av = LongVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -618,7 +618,7 @@ public class LongMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -649,7 +649,7 @@ public class LongMaxVectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
LongVector av = LongVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Short128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Short128VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
ShortVector av = ShortVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Short256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Short256VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
ShortVector av = ShortVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
@@ -611,7 +611,7 @@ public class Short512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
fromMemorySegment(a, index, ByteOrder.nativeOrder(), vmask);
|
||||
@@ -642,7 +642,7 @@ public class Short512VectorLoadStoreTests extends AbstractVectorLoadStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
int index = fi.apply((int) a.byteSize());
|
||||
int index = fi.apply((int) a.byteSize()) & (~(SPECIES.elementSize() - 1));
|
||||
boolean shouldFail = isIndexOutOfBoundsForMask(mask, index, (int) a.byteSize(), SPECIES.elementSize() / 8);
|
||||
try {
|
||||
ShortVector av = ShortVector.fromMemorySegment(SPECIES, a, 0, ByteOrder.nativeOrder());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user