mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-17 02:10:35 +00:00
Merge remote-tracking branch 'jdk/master' into fix-old-reference-processing
This commit is contained in:
commit
88101211f1
@ -458,12 +458,10 @@ char* os::map_memory_to_file(char* base, size_t size, int fd) {
|
||||
warning("Failed mmap to file. (%s)", os::strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
if (base != nullptr && addr != base) {
|
||||
if (!os::release_memory(addr, size)) {
|
||||
warning("Could not release memory on unsuccessful file mapping");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The requested address should be the same as the returned address when using MAP_FIXED
|
||||
// as per POSIX.
|
||||
assert(base == nullptr || addr == base, "base should equal addr when using MAP_FIXED");
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2021 SAP SE. All rights reserved.
|
||||
* Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026 SAP SE. 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
|
||||
@ -946,7 +946,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size
|
||||
if (result == -1 ) break;
|
||||
if (!os::write(fd, &zero_int, 1)) {
|
||||
if (errno == ENOSPC) {
|
||||
warning("Insufficient space for shared memory file:\n %s\nTry using the -Djava.io.tmpdir= option to select an alternate temp location.\n", filename);
|
||||
warning("Insufficient space for shared memory file: %s/%s\n", dirname, filename);
|
||||
}
|
||||
result = OS_ERR;
|
||||
break;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2013 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,8 +26,7 @@
|
||||
#ifndef OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP
|
||||
#define OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read(const void *loc, intx interval) {
|
||||
#if !defined(USE_XLC_BUILTINS)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
|
||||
* Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
@ -27,8 +27,7 @@
|
||||
#ifndef OS_CPU_BSD_AARCH64_PREFETCH_BSD_AARCH64_INLINE_HPP
|
||||
#define OS_CPU_BSD_AARCH64_PREFETCH_BSD_AARCH64_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
if (interval >= 0)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,8 +25,7 @@
|
||||
#ifndef OS_CPU_BSD_X86_PREFETCH_BSD_X86_INLINE_HPP
|
||||
#define OS_CPU_BSD_X86_PREFETCH_BSD_X86_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
__asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval));
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2007, 2008 Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,7 +26,7 @@
|
||||
#ifndef OS_CPU_BSD_ZERO_PREFETCH_BSD_ZERO_INLINE_HPP
|
||||
#define OS_CPU_BSD_ZERO_PREFETCH_BSD_ZERO_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read(const void* loc, intx interval) {
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,8 +26,7 @@
|
||||
#ifndef OS_CPU_LINUX_AARCH64_PREFETCH_LINUX_AARCH64_INLINE_HPP
|
||||
#define OS_CPU_LINUX_AARCH64_PREFETCH_LINUX_AARCH64_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
if (interval >= 0)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,7 +25,7 @@
|
||||
#ifndef OS_CPU_LINUX_ARM_PREFETCH_LINUX_ARM_INLINE_HPP
|
||||
#define OS_CPU_LINUX_ARM_PREFETCH_LINUX_ARM_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
#if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_5TE__)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2013 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,8 +26,7 @@
|
||||
#ifndef OS_CPU_LINUX_PPC_PREFETCH_LINUX_PPC_INLINE_HPP
|
||||
#define OS_CPU_LINUX_PPC_PREFETCH_LINUX_PPC_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read(const void *loc, intx interval) {
|
||||
__asm__ __volatile__ (
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,7 +26,7 @@
|
||||
#ifndef OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP
|
||||
#define OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
if (interval >= 0 && UseZicbop) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,7 +26,7 @@
|
||||
#ifndef OS_CPU_LINUX_S390_PREFETCH_LINUX_S390_INLINE_HPP
|
||||
#define OS_CPU_LINUX_S390_PREFETCH_LINUX_S390_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read(const void* loc, intx interval) {
|
||||
// No prefetch instructions on z/Architecture -> implement trivially.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,8 +25,7 @@
|
||||
#ifndef OS_CPU_LINUX_X86_PREFETCH_LINUX_X86_INLINE_HPP
|
||||
#define OS_CPU_LINUX_X86_PREFETCH_LINUX_X86_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
__asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval));
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2007, 2008 Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,7 +26,7 @@
|
||||
#ifndef OS_CPU_LINUX_ZERO_PREFETCH_LINUX_ZERO_INLINE_HPP
|
||||
#define OS_CPU_LINUX_ZERO_PREFETCH_LINUX_ZERO_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read(const void* loc, intx interval) {
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Microsoft Corporation. All rights reserved.
|
||||
* Copyright (c) 2020, 2026, Microsoft Corporation. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,8 +25,7 @@
|
||||
#ifndef OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP
|
||||
#define OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,7 +25,7 @@
|
||||
#ifndef OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP
|
||||
#define OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {}
|
||||
inline void Prefetch::write(void *loc, intx interval) {}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -686,7 +686,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) {
|
||||
// the check before we do the actual allocation. The reason for doing it
|
||||
// before the allocation is that we avoid having to keep track of the newly
|
||||
// allocated memory while we do a GC.
|
||||
if (policy()->need_to_start_conc_mark("concurrent humongous allocation",
|
||||
// Only try that if we can actually perform a GC.
|
||||
if (is_init_completed() && policy()->need_to_start_conc_mark("concurrent humongous allocation",
|
||||
word_size)) {
|
||||
try_collect(word_size, GCCause::_g1_humongous_allocation, collection_counters(this));
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -46,7 +46,7 @@
|
||||
#include "oops/access.inline.hpp"
|
||||
#include "oops/compressedOops.inline.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/prefetch.hpp"
|
||||
#include "runtime/prefetch.inline.hpp"
|
||||
#include "runtime/threads.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "utilities/bitMap.inline.hpp"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,6 +28,7 @@
|
||||
#include "gc/serial/serialHeap.inline.hpp"
|
||||
#include "gc/shared/space.hpp"
|
||||
#include "memory/iterator.inline.hpp"
|
||||
#include "runtime/prefetch.inline.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
|
||||
void CardTableRS::scan_old_to_young_refs(TenuredGeneration* tg, HeapWord* saved_top) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -34,7 +34,6 @@
|
||||
#include "memory/virtualspace.hpp"
|
||||
#include "runtime/mutex.hpp"
|
||||
#include "runtime/perfData.hpp"
|
||||
#include "runtime/prefetch.inline.hpp"
|
||||
|
||||
// A Generation models a heap area for similarly-aged objects.
|
||||
// It will contain one ore more spaces holding the actual objects.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -70,6 +70,7 @@
|
||||
#include "runtime/init.hpp"
|
||||
#include "runtime/java.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/prefetch.inline.hpp"
|
||||
#include "runtime/threads.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "services/memoryManager.hpp"
|
||||
|
||||
@ -700,7 +700,7 @@ void OopStorage::Block::release_entries(uintx releasing, OopStorage* owner) {
|
||||
// then someone else has made such a claim and the deferred update has not
|
||||
// yet been processed and will include our change, so we don't need to do
|
||||
// anything further.
|
||||
if (_deferred_updates_next.compare_exchange(nullptr, this) == nullptr) {
|
||||
if (_deferred_updates_next.compare_set(nullptr, this)) {
|
||||
// Successfully claimed. Push, with self-loop for end-of-list.
|
||||
Block* head = owner->_deferred_updates.load_relaxed();
|
||||
while (true) {
|
||||
|
||||
@ -56,7 +56,7 @@ void PretouchTask::work(uint worker_id) {
|
||||
char* cur_end = cur_start + MIN2(_chunk_size, pointer_delta(_end_addr, cur_start, 1));
|
||||
if (cur_start >= cur_end) {
|
||||
break;
|
||||
} else if (cur_start == _cur_addr.compare_exchange(cur_start, cur_end)) {
|
||||
} else if (_cur_addr.compare_set(cur_start, cur_end)) {
|
||||
os::pretouch_memory(cur_start, cur_end, _page_size);
|
||||
} // Else attempt to claim chunk failed, so try again.
|
||||
}
|
||||
|
||||
@ -183,8 +183,8 @@ protected:
|
||||
_age.store_relaxed(new_age);
|
||||
}
|
||||
|
||||
Age cmpxchg_age(Age old_age, Age new_age) {
|
||||
return _age.compare_exchange(old_age, new_age);
|
||||
bool par_set_age(Age old_age, Age new_age) {
|
||||
return _age.compare_set(old_age, new_age);
|
||||
}
|
||||
|
||||
idx_t age_top_relaxed() const {
|
||||
@ -345,7 +345,7 @@ protected:
|
||||
|
||||
using TaskQueueSuper<N, MT>::age_relaxed;
|
||||
using TaskQueueSuper<N, MT>::set_age_relaxed;
|
||||
using TaskQueueSuper<N, MT>::cmpxchg_age;
|
||||
using TaskQueueSuper<N, MT>::par_set_age;
|
||||
using TaskQueueSuper<N, MT>::age_top_relaxed;
|
||||
|
||||
using TaskQueueSuper<N, MT>::increment_index;
|
||||
|
||||
@ -170,8 +170,7 @@ bool GenericTaskQueue<E, MT, N>::pop_local_slow(uint localBot, Age oldAge) {
|
||||
if (localBot == oldAge.top()) {
|
||||
// No competing pop_global has yet incremented "top"; we'll try to
|
||||
// install new_age, thus claiming the element.
|
||||
Age tempAge = cmpxchg_age(oldAge, newAge);
|
||||
if (tempAge == oldAge) {
|
||||
if (par_set_age(oldAge, newAge)) {
|
||||
// We win.
|
||||
assert_not_underflow(localBot, age_top_relaxed());
|
||||
TASKQUEUE_STATS_ONLY(stats.record_pop_slow());
|
||||
@ -283,12 +282,12 @@ typename GenericTaskQueue<E, MT, N>::PopResult GenericTaskQueue<E, MT, N>::pop_g
|
||||
idx_t new_top = increment_index(oldAge.top());
|
||||
idx_t new_tag = oldAge.tag() + ((new_top == 0) ? 1 : 0);
|
||||
Age newAge(new_top, new_tag);
|
||||
Age resAge = cmpxchg_age(oldAge, newAge);
|
||||
bool result = par_set_age(oldAge, newAge);
|
||||
|
||||
// Note that using "bottom" here might fail, since a pop_local might
|
||||
// have decremented it.
|
||||
assert_not_underflow(localBot, newAge.top());
|
||||
return resAge == oldAge ? PopResult::Success : PopResult::Contended;
|
||||
return result ? PopResult::Success : PopResult::Contended;
|
||||
}
|
||||
|
||||
inline int randomParkAndMiller(int *seed0) {
|
||||
|
||||
@ -61,7 +61,12 @@ ShenandoahGenerationalControlThread::ShenandoahGenerationalControlThread() :
|
||||
|
||||
void ShenandoahGenerationalControlThread::run_service() {
|
||||
|
||||
// This is the only instance of request. It is important that request.generation
|
||||
// does not change between a concurrent cycle failure and the start of a degenerated
|
||||
// cycle. We initialize it with the young generation to handle the pathological case
|
||||
// where the very first cycle is degenerated (some tests exercise this path).
|
||||
ShenandoahGCRequest request;
|
||||
request.generation = _heap->young_generation();
|
||||
while (!should_terminate()) {
|
||||
|
||||
// Figure out if we have pending requests.
|
||||
@ -77,12 +82,10 @@ void ShenandoahGenerationalControlThread::run_service() {
|
||||
|
||||
// If the cycle was cancelled, continue the next iteration to deal with it. Otherwise,
|
||||
// if there was no other cycle requested, cleanup and wait for the next request.
|
||||
if (!_heap->cancelled_gc()) {
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (_requested_gc_cause == GCCause::_no_gc) {
|
||||
set_gc_mode(ml, none);
|
||||
ml.wait();
|
||||
}
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (_requested_gc_cause == GCCause::_no_gc) {
|
||||
set_gc_mode(ml, none);
|
||||
ml.wait();
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,8 +99,7 @@ void ShenandoahGenerationalControlThread::stop_service() {
|
||||
log_debug(gc, thread)("Stopping control thread");
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
_heap->cancel_gc(GCCause::_shenandoah_stop_vm);
|
||||
_requested_gc_cause = GCCause::_shenandoah_stop_vm;
|
||||
notify_cancellation(ml, GCCause::_shenandoah_stop_vm);
|
||||
notify_control_thread(ml, GCCause::_shenandoah_stop_vm);
|
||||
// We can't wait here because it may interfere with the active cycle's ability
|
||||
// to reach a safepoint (this runs on a java thread).
|
||||
}
|
||||
@ -105,29 +107,39 @@ void ShenandoahGenerationalControlThread::stop_service() {
|
||||
void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest& request) {
|
||||
// Hold the lock while we read request cause and generation
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (_heap->cancelled_gc()) {
|
||||
// The previous request was cancelled. Either it was cancelled for an allocation
|
||||
// failure (degenerated cycle), or old marking was cancelled to run a young collection.
|
||||
// In either case, the correct generation for the next cycle can be determined by
|
||||
// the cancellation cause.
|
||||
request.cause = _heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc);
|
||||
if (request.cause == GCCause::_shenandoah_concurrent_gc) {
|
||||
|
||||
log_debug(gc, thread)("cancelled cause: %s, requested cause: %s",
|
||||
GCCause::to_string(_heap->cancelled_cause()), GCCause::to_string(_requested_gc_cause));
|
||||
|
||||
request.cause = _requested_gc_cause;
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(request.cause)) {
|
||||
if (_degen_point == ShenandoahGC::_degenerated_unset) {
|
||||
request.generation = _heap->young_generation();
|
||||
_degen_point = ShenandoahGC::_degenerated_outside_cycle;
|
||||
} else {
|
||||
assert(request.generation != nullptr, "Must know which generation to use for degenerated cycle");
|
||||
}
|
||||
} else {
|
||||
request.cause = _requested_gc_cause;
|
||||
if (request.cause == GCCause::_shenandoah_concurrent_gc) {
|
||||
// This is a regulator request. It is also possible that the regulator "canceled" an old mark,
|
||||
// so we can clear that here. This clear operation will only clear the cancellation if it is
|
||||
// a regulator request.
|
||||
_heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc);
|
||||
}
|
||||
request.generation = _requested_generation;
|
||||
|
||||
// Only clear these if we made a request from them. In the case of a cancelled gc,
|
||||
// we do not want to inadvertently lose this pending request.
|
||||
_requested_gc_cause = GCCause::_no_gc;
|
||||
_requested_generation = nullptr;
|
||||
}
|
||||
|
||||
log_debug(gc, thread)("request.cause: %s, request.generation: %s",
|
||||
GCCause::to_string(request.cause), request.generation == nullptr ? "None" : request.generation->name());
|
||||
|
||||
_requested_gc_cause = GCCause::_no_gc;
|
||||
_requested_generation = nullptr;
|
||||
|
||||
if (request.cause == GCCause::_no_gc || request.cause == GCCause::_shenandoah_stop_vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(request.generation != nullptr, "request.generation cannot be null, cause is: %s", GCCause::to_string(request.cause));
|
||||
GCMode mode;
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(request.cause)) {
|
||||
mode = prepare_for_allocation_failure_gc(request);
|
||||
@ -140,11 +152,9 @@ void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest&
|
||||
}
|
||||
|
||||
ShenandoahGenerationalControlThread::GCMode ShenandoahGenerationalControlThread::prepare_for_allocation_failure_gc(ShenandoahGCRequest &request) {
|
||||
|
||||
if (_degen_point == ShenandoahGC::_degenerated_unset) {
|
||||
_degen_point = ShenandoahGC::_degenerated_outside_cycle;
|
||||
request.generation = _heap->young_generation();
|
||||
} else if (request.generation->is_old()) {
|
||||
// Important: not all paths update the request.generation. This is intentional.
|
||||
// A degenerated cycle must use the same generation carried over from the previous request.
|
||||
if (request.generation->is_old()) {
|
||||
// This means we degenerated during the young bootstrap for the old generation
|
||||
// cycle. The following degenerated cycle should therefore also be young.
|
||||
request.generation = _heap->young_generation();
|
||||
@ -583,6 +593,8 @@ bool ShenandoahGenerationalControlThread::check_cancellation_or_degen(Shenandoah
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(_heap->cancelled_cause())) {
|
||||
assert(_degen_point == ShenandoahGC::_degenerated_unset,
|
||||
"Should not be set yet: %s", ShenandoahGC::degen_point_to_string(_degen_point));
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
_requested_gc_cause = _heap->cancelled_cause();
|
||||
_degen_point = point;
|
||||
log_debug(gc, thread)("Cancellation detected:, reason: %s, degen point: %s",
|
||||
GCCause::to_string(_heap->cancelled_cause()),
|
||||
@ -628,9 +640,7 @@ void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const Sh
|
||||
|
||||
void ShenandoahGenerationalControlThread::request_gc(GCCause::Cause cause) {
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(cause)) {
|
||||
// GC should already be cancelled. Here we are just notifying the control thread to
|
||||
// wake up and handle the cancellation request, so we don't need to set _requested_gc_cause.
|
||||
notify_cancellation(cause);
|
||||
notify_control_thread(cause);
|
||||
} else if (ShenandoahCollectorPolicy::should_handle_requested_gc(cause)) {
|
||||
handle_requested_gc(cause);
|
||||
}
|
||||
@ -648,7 +658,8 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (gc_mode() == servicing_old) {
|
||||
if (!preempt_old_marking(generation)) {
|
||||
log_debug(gc, thread)("Cannot start young, old collection is not preemptible");
|
||||
// Global should be able to cause old collection to be abandoned
|
||||
log_debug(gc, thread)("Cannot start %s, old collection is not preemptible", generation->name());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -656,7 +667,7 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera
|
||||
log_info(gc)("Preempting old generation mark to allow %s GC", generation->name());
|
||||
while (gc_mode() == servicing_old) {
|
||||
ShenandoahHeap::heap()->cancel_gc(GCCause::_shenandoah_concurrent_gc);
|
||||
notify_cancellation(ml, GCCause::_shenandoah_concurrent_gc);
|
||||
notify_control_thread(ml, GCCause::_shenandoah_concurrent_gc, generation);
|
||||
ml.wait();
|
||||
}
|
||||
return true;
|
||||
@ -690,21 +701,34 @@ void ShenandoahGenerationalControlThread::notify_control_thread(GCCause::Cause c
|
||||
|
||||
void ShenandoahGenerationalControlThread::notify_control_thread(MonitorLocker& ml, GCCause::Cause cause, ShenandoahGeneration* generation) {
|
||||
assert(_control_lock.is_locked(), "Request lock must be held here");
|
||||
log_debug(gc, thread)("Notify control (%s): %s, %s", gc_mode_name(gc_mode()), GCCause::to_string(cause), generation->name());
|
||||
_requested_gc_cause = cause;
|
||||
_requested_generation = generation;
|
||||
ml.notify();
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(_requested_gc_cause)) {
|
||||
// We have already observed a request to handle an allocation failure. We cannot allow
|
||||
// another request (System.gc or regulator) to subvert the degenerated cycle.
|
||||
log_debug(gc, thread)("Not overwriting gc cause %s with %s", GCCause::to_string(_requested_gc_cause), GCCause::to_string(cause));
|
||||
} else {
|
||||
log_debug(gc, thread)("Notify control (%s): %s, %s", gc_mode_name(gc_mode()), GCCause::to_string(cause), generation->name());
|
||||
_requested_gc_cause = cause;
|
||||
_requested_generation = generation;
|
||||
ml.notify();
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahGenerationalControlThread::notify_cancellation(GCCause::Cause cause) {
|
||||
void ShenandoahGenerationalControlThread::notify_control_thread(GCCause::Cause cause) {
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
notify_cancellation(ml, cause);
|
||||
notify_control_thread(ml, cause);
|
||||
}
|
||||
|
||||
void ShenandoahGenerationalControlThread::notify_cancellation(MonitorLocker& ml, GCCause::Cause cause) {
|
||||
assert(_heap->cancelled_gc(), "GC should already be cancelled");
|
||||
log_debug(gc,thread)("Notify control (%s): %s", gc_mode_name(gc_mode()), GCCause::to_string(cause));
|
||||
ml.notify();
|
||||
void ShenandoahGenerationalControlThread::notify_control_thread(MonitorLocker& ml, GCCause::Cause cause) {
|
||||
assert(_control_lock.is_locked(), "Request lock must be held here");
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(_requested_gc_cause)) {
|
||||
// We have already observed a request to handle an allocation failure. We cannot allow
|
||||
// another request (System.gc or regulator) to subvert the degenerated cycle.
|
||||
log_debug(gc, thread)("Not overwriting gc cause %s with %s", GCCause::to_string(_requested_gc_cause), GCCause::to_string(cause));
|
||||
} else {
|
||||
log_debug(gc, thread)("Notify control (%s): %s", gc_mode_name(gc_mode()), GCCause::to_string(cause));
|
||||
_requested_gc_cause = cause;
|
||||
ml.notify();
|
||||
}
|
||||
}
|
||||
|
||||
bool ShenandoahGenerationalControlThread::preempt_old_marking(ShenandoahGeneration* generation) {
|
||||
|
||||
@ -138,16 +138,13 @@ private:
|
||||
// Return printable name for the given gc mode.
|
||||
static const char* gc_mode_name(GCMode mode);
|
||||
|
||||
// Takes the request lock and updates the requested cause and generation, then notifies the control thread.
|
||||
// The overloaded variant should be used when the _control_lock is already held.
|
||||
// These notify the control thread after updating _requested_gc_cause and (optionally) _requested_generation.
|
||||
// Updating the requested generation is not necessary for allocation failures nor when stopping the thread.
|
||||
void notify_control_thread(GCCause::Cause cause);
|
||||
void notify_control_thread(MonitorLocker& ml, GCCause::Cause cause);
|
||||
void notify_control_thread(GCCause::Cause cause, ShenandoahGeneration* generation);
|
||||
void notify_control_thread(MonitorLocker& ml, GCCause::Cause cause, ShenandoahGeneration* generation);
|
||||
|
||||
// Notifies the control thread, but does not update the requested cause or generation.
|
||||
// The overloaded variant should be used when the _control_lock is already held.
|
||||
void notify_cancellation(GCCause::Cause cause);
|
||||
void notify_cancellation(MonitorLocker& ml, GCCause::Cause cause);
|
||||
|
||||
// Configure the heap to age objects and regions if the aging period has elapsed.
|
||||
void maybe_set_aging_cycle();
|
||||
|
||||
|
||||
@ -46,13 +46,13 @@ public:
|
||||
bool start_old_collection();
|
||||
|
||||
static ShenandoahGenerationalHeap* heap() {
|
||||
assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap");
|
||||
assert(ShenandoahCardBarrier, "Should have card barrier to use generational heap");
|
||||
CollectedHeap* heap = Universe::heap();
|
||||
return cast(heap);
|
||||
}
|
||||
|
||||
static ShenandoahGenerationalHeap* cast(CollectedHeap* heap) {
|
||||
assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap");
|
||||
assert(ShenandoahCardBarrier, "Should have card barrier to use generational heap");
|
||||
return checked_cast<ShenandoahGenerationalHeap*>(heap);
|
||||
}
|
||||
|
||||
|
||||
@ -149,6 +149,13 @@ bool ShenandoahRegulatorThread::start_global_cycle() const {
|
||||
|
||||
bool ShenandoahRegulatorThread::request_concurrent_gc(ShenandoahGeneration* generation) const {
|
||||
double now = os::elapsedTime();
|
||||
|
||||
// This call may find the control thread waiting on workers which have suspended
|
||||
// to allow a safepoint to run. If this regulator thread does not yield, the safepoint
|
||||
// will not run. The worker threads won't progress, the control thread won't progress,
|
||||
// and the regulator thread may never yield. Therefore, we leave the suspendible
|
||||
// thread set before making this call.
|
||||
SuspendibleThreadSetLeaver leaver;
|
||||
bool accepted = _control_thread->request_concurrent_gc(generation);
|
||||
if (LogTarget(Debug, gc, thread)::is_enabled() && accepted) {
|
||||
double wait_time = os::elapsedTime() - now;
|
||||
|
||||
@ -1112,8 +1112,6 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if( !ti->is_con() ) return nullptr;
|
||||
jint con = ti->get_con();
|
||||
|
||||
Node *hook = new Node(1);
|
||||
|
||||
// First, special check for modulo 2^k-1
|
||||
if( con >= 0 && con < max_jint && is_power_of_2(con+1) ) {
|
||||
uint k = exact_log2(con+1); // Extract k
|
||||
@ -1129,7 +1127,9 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
Node *x = in(1); // Value being mod'd
|
||||
Node *divisor = in(2); // Also is mask
|
||||
|
||||
hook->init_req(0, x); // Add a use to x to prevent him from dying
|
||||
// Add a use to x to prevent it from dying
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, x);
|
||||
// Generate code to reduce X rapidly to nearly 2^k-1.
|
||||
for( int i = 0; i < trip_count; i++ ) {
|
||||
Node *xl = phase->transform( new AndINode(x,divisor) );
|
||||
@ -1185,6 +1185,7 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
// Save in(1) so that it cannot be changed or deleted
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, in(1));
|
||||
|
||||
// Divide using the transform from DivI to MulL
|
||||
@ -1407,8 +1408,6 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if( !tl->is_con() ) return nullptr;
|
||||
jlong con = tl->get_con();
|
||||
|
||||
Node *hook = new Node(1);
|
||||
|
||||
// Expand mod
|
||||
if(con >= 0 && con < max_jlong && is_power_of_2(con + 1)) {
|
||||
uint k = log2i_exact(con + 1); // Extract k
|
||||
@ -1426,13 +1425,15 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
Node *x = in(1); // Value being mod'd
|
||||
Node *divisor = in(2); // Also is mask
|
||||
|
||||
hook->init_req(0, x); // Add a use to x to prevent him from dying
|
||||
// Add a use to x to prevent it from dying
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, x);
|
||||
// Generate code to reduce X rapidly to nearly 2^k-1.
|
||||
for( int i = 0; i < trip_count; i++ ) {
|
||||
Node *xl = phase->transform( new AndLNode(x,divisor) );
|
||||
Node *xh = phase->transform( new RShiftLNode(x,phase->intcon(k)) ); // Must be signed
|
||||
x = phase->transform( new AddLNode(xh,xl) );
|
||||
hook->set_req(0, x); // Add a use to x to prevent him from dying
|
||||
hook->set_req(0, x); // Add a use to x to prevent it from dying
|
||||
}
|
||||
|
||||
// Generate sign-fixup code. Was original value positive?
|
||||
@ -1482,6 +1483,8 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
// Save in(1) so that it cannot be changed or deleted
|
||||
// Add a use to x to prevent him from dying
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, in(1));
|
||||
|
||||
// Divide using the transform from DivL to MulL
|
||||
|
||||
@ -75,6 +75,7 @@
|
||||
// v.release_store(x) -> void
|
||||
// v.release_store_fence(x) -> void
|
||||
// v.compare_exchange(x, y [, o]) -> T
|
||||
// v.compare_set(x, y [, o]) -> bool
|
||||
// v.exchange(x [, o]) -> T
|
||||
//
|
||||
// (2) All atomic types are default constructible.
|
||||
@ -267,6 +268,11 @@ public:
|
||||
return AtomicAccess::cmpxchg(value_ptr(), compare_value, new_value, order);
|
||||
}
|
||||
|
||||
bool compare_set(T compare_value, T new_value,
|
||||
atomic_memory_order order = memory_order_conservative) {
|
||||
return compare_exchange(compare_value, new_value, order) == compare_value;
|
||||
}
|
||||
|
||||
T exchange(T new_value,
|
||||
atomic_memory_order order = memory_order_conservative) {
|
||||
return AtomicAccess::xchg(this->value_ptr(), new_value, order);
|
||||
@ -479,6 +485,13 @@ public:
|
||||
order));
|
||||
}
|
||||
|
||||
bool compare_set(T compare_value, T new_value,
|
||||
atomic_memory_order order = memory_order_conservative) {
|
||||
return _value.compare_set(decay(compare_value),
|
||||
decay(new_value),
|
||||
order);
|
||||
}
|
||||
|
||||
T exchange(T new_value, atomic_memory_order order = memory_order_conservative) {
|
||||
return recover(_value.exchange(decay(new_value), order));
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,9 +25,24 @@
|
||||
#ifndef SHARE_RUNTIME_PREFETCH_INLINE_HPP
|
||||
#define SHARE_RUNTIME_PREFETCH_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
|
||||
// If calls to prefetch methods are in a loop, the loop should be cloned
|
||||
// such that if Prefetch{Scan,Copy}Interval and/or PrefetchFieldInterval
|
||||
// say not to do prefetching, these methods aren't called. At the very
|
||||
// least, they take up a memory issue slot. They should be implemented
|
||||
// as inline assembly code: doing an actual call isn't worth the cost.
|
||||
|
||||
class Prefetch : AllStatic {
|
||||
public:
|
||||
// Prefetch anticipating read; must not fault, semantically a no-op
|
||||
static void read(const void* loc, intx interval);
|
||||
|
||||
// Prefetch anticipating write; must not fault, semantically a no-op
|
||||
static void write(void* loc, intx interval);
|
||||
};
|
||||
|
||||
#include OS_CPU_HEADER_INLINE(prefetch)
|
||||
|
||||
#endif // SHARE_RUNTIME_PREFETCH_INLINE_HPP
|
||||
|
||||
@ -157,7 +157,7 @@ inline bool ConcurrentHashTable<CONFIG, MT>::
|
||||
if (is_locked()) {
|
||||
return false;
|
||||
}
|
||||
if (_first.compare_exchange(expect, node) == expect) {
|
||||
if (_first.compare_set(expect, node)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -172,7 +172,7 @@ inline bool ConcurrentHashTable<CONFIG, MT>::
|
||||
}
|
||||
// We will expect a clean first pointer.
|
||||
Node* tmp = first();
|
||||
if (_first.compare_exchange(tmp, set_state(tmp, STATE_LOCK_BIT)) == tmp) {
|
||||
if (_first.compare_set(tmp, set_state(tmp, STATE_LOCK_BIT))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -181,7 +181,7 @@ void GenericWaitBarrier::Cell::disarm(int32_t expected_tag) {
|
||||
tag, waiters);
|
||||
|
||||
int64_t new_state = encode(0, waiters);
|
||||
if (_state.compare_exchange(state, new_state) == state) {
|
||||
if (_state.compare_set(state, new_state)) {
|
||||
// Successfully disarmed.
|
||||
break;
|
||||
}
|
||||
@ -218,7 +218,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) {
|
||||
tag, waiters);
|
||||
|
||||
int64_t new_state = encode(tag, waiters + 1);
|
||||
if (_state.compare_exchange(state, new_state) == state) {
|
||||
if (_state.compare_set(state, new_state)) {
|
||||
// Success! Proceed to wait.
|
||||
break;
|
||||
}
|
||||
@ -247,7 +247,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) {
|
||||
tag, waiters);
|
||||
|
||||
int64_t new_state = encode(tag, waiters - 1);
|
||||
if (_state.compare_exchange(state, new_state) == state) {
|
||||
if (_state.compare_set(state, new_state)) {
|
||||
// Success!
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2045,19 +2045,26 @@ public final class String
|
||||
return encode(Charset.defaultCharset(), coder(), value);
|
||||
}
|
||||
|
||||
boolean bytesCompatible(Charset charset) {
|
||||
boolean bytesCompatible(Charset charset, int srcIndex, int numChars) {
|
||||
if (isLatin1()) {
|
||||
if (charset == ISO_8859_1.INSTANCE) {
|
||||
return true; // ok, same encoding
|
||||
} else if (charset == UTF_8.INSTANCE || charset == US_ASCII.INSTANCE) {
|
||||
return !StringCoding.hasNegatives(value, 0, value.length); // ok, if ASCII-compatible
|
||||
return !StringCoding.hasNegatives(value, srcIndex, numChars); // ok, if ASCII-compatible
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void copyToSegmentRaw(MemorySegment segment, long offset) {
|
||||
MemorySegment.copy(value, 0, segment, ValueLayout.JAVA_BYTE, offset, value.length);
|
||||
void copyToSegmentRaw(MemorySegment segment, long offset, int srcIndex, int srcLength) {
|
||||
if (!isLatin1()) {
|
||||
// This method is intended to be used together with bytesCompatible, which currently only supports
|
||||
// latin1 strings. In the future, bytesCompatible could be updated to handle more cases, like
|
||||
// UTF-16 strings (when the platform and charset endianness match, and the String doesn’t contain
|
||||
// unpaired surrogates). If that happens, copyToSegmentRaw should also be updated.
|
||||
throw new IllegalStateException("This string does not support copyToSegmentRaw");
|
||||
}
|
||||
MemorySegment.copy(value, srcIndex, segment, ValueLayout.JAVA_BYTE, offset, srcLength);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -2331,13 +2331,13 @@ public final class System {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyToSegmentRaw(String string, MemorySegment segment, long offset) {
|
||||
string.copyToSegmentRaw(segment, offset);
|
||||
public void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) {
|
||||
string.copyToSegmentRaw(segment, offset, srcIndex, srcLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bytesCompatible(String string, Charset charset) {
|
||||
return string.bytesCompatible(charset);
|
||||
public boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) {
|
||||
return string.bytesCompatible(charset, srcIndex, numChars);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -31,6 +31,8 @@ import java.lang.classfile.Instruction;
|
||||
import java.lang.classfile.Opcode;
|
||||
|
||||
import jdk.internal.classfile.impl.AbstractInstruction;
|
||||
import jdk.internal.classfile.impl.BytecodeHelpers;
|
||||
import jdk.internal.classfile.impl.Util;
|
||||
|
||||
/**
|
||||
* Models a local variable increment instruction in the {@code code} array of a
|
||||
@ -82,6 +84,37 @@ public sealed interface IncrementInstruction extends Instruction
|
||||
* @throws IllegalArgumentException if {@code slot} or {@code constant} is out of range
|
||||
*/
|
||||
static IncrementInstruction of(int slot, int constant) {
|
||||
return new AbstractInstruction.UnboundIncrementInstruction(slot, constant);
|
||||
var opcode = BytecodeHelpers.validateAndIsWideIinc(slot, constant) ? Opcode.IINC_W: Opcode.IINC;
|
||||
return new AbstractInstruction.UnboundIncrementInstruction(opcode, slot, constant);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return an increment instruction}
|
||||
* <p>
|
||||
* {@code slot} must be {@link java.lang.classfile##u1 u1} and
|
||||
* {@code constant} must be within {@code [-128, 127]} for
|
||||
* {@link Opcode#IINC iinc}, or {@code slot} must be
|
||||
* {@link java.lang.classfile##u2 u2} and {@code constant} must be
|
||||
* within {@code [-32768, 32767]} for {@link Opcode#IINC_W wide iinc}.
|
||||
*
|
||||
* @apiNote
|
||||
* The explicit {@code op} argument allows creating {@code wide} or
|
||||
* regular increment instructions when {@code slot} and
|
||||
* {@code constant} can be encoded with more optimized
|
||||
* increment instructions.
|
||||
*
|
||||
* @param op the opcode for the specific type of increment instruction,
|
||||
* which must be of kind {@link Opcode.Kind#INCREMENT}
|
||||
* @param slot the local variable slot to increment
|
||||
* @param constant the increment constant
|
||||
* @throws IllegalArgumentException if the opcode kind is not
|
||||
* {@link Opcode.Kind#INCREMENT} or {@code slot} or
|
||||
* {@code constant} is out of range
|
||||
* @since 27
|
||||
*/
|
||||
static IncrementInstruction of(Opcode op, int slot, int constant) {
|
||||
Util.checkKind(op, Opcode.Kind.INCREMENT);
|
||||
BytecodeHelpers.validateIncrement(op, slot, constant);
|
||||
return new AbstractInstruction.UnboundIncrementInstruction(op, slot, constant);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1296,12 +1296,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
* over the decoding process is required.
|
||||
* <p>
|
||||
* Getting a string from a segment with a known byte offset and
|
||||
* known byte length can be done like so:
|
||||
* {@snippet lang=java :
|
||||
* byte[] bytes = new byte[length];
|
||||
* MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, length);
|
||||
* return new String(bytes, charset);
|
||||
* }
|
||||
* known byte length can be done using {@link #getString(long, Charset, long)}.
|
||||
*
|
||||
* @param offset offset in bytes (relative to this segment address) at which this
|
||||
* access operation will occur
|
||||
@ -1328,6 +1323,40 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
*/
|
||||
String getString(long offset, Charset charset);
|
||||
|
||||
/**
|
||||
* Reads a string from this segment at the given offset, using the provided length
|
||||
* and charset.
|
||||
* <p>
|
||||
* This method always replaces malformed-input and unmappable-character
|
||||
* sequences with this charset's default replacement string. The {@link
|
||||
* java.nio.charset.CharsetDecoder} class should be used when more control
|
||||
* over the decoding process is required.
|
||||
* <p>
|
||||
* If the string contains any {@code '\0'} characters, they will be read as well.
|
||||
* This differs from {@link #getString(long, Charset)}, which will only read up
|
||||
* to the first {@code '\0'}, resulting in truncation for string data that contains
|
||||
* the {@code '\0'} character.
|
||||
*
|
||||
* @param offset offset in bytes (relative to this segment address) at which this
|
||||
* access operation will occur
|
||||
* @param charset the charset used to {@linkplain Charset#newDecoder() decode} the
|
||||
* string bytes
|
||||
* @param byteLength length, in bytes, of the region of memory to read and decode into
|
||||
* a string
|
||||
* @return a Java string constructed from the bytes read from the given starting
|
||||
* address up to the given length
|
||||
* @throws IllegalArgumentException if the size of the string is greater than the
|
||||
* largest string supported by the platform
|
||||
* @throws IndexOutOfBoundsException if {@code offset < 0}
|
||||
* @throws IndexOutOfBoundsException if {@code offset > byteSize() - byteLength}
|
||||
* @throws IllegalStateException if the {@linkplain #scope() scope} associated with
|
||||
* this segment is not {@linkplain Scope#isAlive() alive}
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code isAccessibleBy(T) == false}
|
||||
* @throws IllegalArgumentException if {@code byteLength < 0}
|
||||
*/
|
||||
String getString(long offset, Charset charset, long byteLength);
|
||||
|
||||
/**
|
||||
* Writes the given string into this segment at the given offset, converting it to
|
||||
* a null-terminated byte sequence using the {@linkplain StandardCharsets#UTF_8 UTF-8}
|
||||
@ -1366,7 +1395,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again.
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link #getString(long, Charset, long)}.
|
||||
*
|
||||
* @param offset offset in bytes (relative to this segment address) at which this
|
||||
* access operation will occur, the final address of this write
|
||||
@ -2606,6 +2636,50 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
elementCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the byte sequence of the given string encoded using the provided charset
|
||||
* to the destination segment.
|
||||
* <p>
|
||||
* This method always replaces malformed-input and unmappable-character
|
||||
* sequences with this charset's default replacement string. The {@link
|
||||
* java.nio.charset.CharsetDecoder} class should be used when more control
|
||||
* over the decoding process is required.
|
||||
* <p>
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link #getString(long, Charset, long)}.
|
||||
*
|
||||
* @param src the Java string to be written into the destination segment
|
||||
* @param dstEncoding the charset used to {@linkplain Charset#newEncoder() encode}
|
||||
* the string bytes.
|
||||
* @param srcIndex the starting character index of the source string
|
||||
* @param dst the destination segment
|
||||
* @param dstOffset the starting offset, in bytes, of the destination segment
|
||||
* @param numChars the number of characters to be copied
|
||||
* @throws IllegalStateException if the {@linkplain #scope() scope} associated with
|
||||
* {@code dst} is not {@linkplain Scope#isAlive() alive}
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code dst.isAccessibleBy(T) == false}
|
||||
* @throws IndexOutOfBoundsException if either {@code srcIndex}, {@code numChars}, or {@code dstOffset}
|
||||
* are {@code < 0}
|
||||
* @throws IndexOutOfBoundsException if {@code srcIndex > src.length() - numChars}
|
||||
* @throws IllegalArgumentException if {@code dst} is {@linkplain #isReadOnly() read-only}
|
||||
* @throws IndexOutOfBoundsException if {@code dstOffset > dstSegment.byteSize() - B} where {@code B} is the size,
|
||||
* in bytes, of the substring of {@code src} encoded using the given charset
|
||||
* @return the number of copied bytes.
|
||||
*/
|
||||
@ForceInline
|
||||
static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) {
|
||||
Objects.requireNonNull(src);
|
||||
Objects.requireNonNull(dstEncoding);
|
||||
Objects.requireNonNull(dst);
|
||||
Objects.checkFromIndexSize(srcIndex, numChars, src.length());
|
||||
|
||||
return AbstractMemorySegmentImpl.copy(src, dstEncoding, srcIndex, dst, dstOffset, numChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns the relative offset, in bytes, of the first mismatch between the
|
||||
* source and the destination segments. More specifically, the bytes at offset
|
||||
|
||||
@ -111,7 +111,8 @@ public interface SegmentAllocator {
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again.
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link MemorySegment#getString(long, Charset, long)}.
|
||||
*
|
||||
* @param str the Java string to be converted into a C string
|
||||
* @param charset the charset used to {@linkplain Charset#newEncoder() encode} the
|
||||
@ -137,10 +138,10 @@ public interface SegmentAllocator {
|
||||
int termCharSize = StringSupport.CharsetKind.of(charset).terminatorCharSize();
|
||||
MemorySegment segment;
|
||||
int length;
|
||||
if (StringSupport.bytesCompatible(str, charset)) {
|
||||
if (StringSupport.bytesCompatible(str, charset, 0, str.length())) {
|
||||
length = str.length();
|
||||
segment = allocateNoInit((long) length + termCharSize);
|
||||
StringSupport.copyToSegmentRaw(str, segment, 0);
|
||||
StringSupport.copyToSegmentRaw(str, segment, 0, 0, str.length());
|
||||
} else {
|
||||
byte[] bytes = str.getBytes(charset);
|
||||
length = bytes.length;
|
||||
@ -153,6 +154,53 @@ public interface SegmentAllocator {
|
||||
return segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a Java string using the provided charset and stores the resulting
|
||||
* byte array into a memory segment.
|
||||
* <p>
|
||||
* This method always replaces malformed-input and unmappable-character
|
||||
* sequences with this charset's default replacement byte array. The
|
||||
* {@link java.nio.charset.CharsetEncoder} class should be used when more
|
||||
* control over the encoding process is required.
|
||||
* <p>
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link MemorySegment#getString(long, Charset, long)}.
|
||||
*
|
||||
* @param str the Java string to be encoded
|
||||
* @param charset the charset used to {@linkplain Charset#newEncoder() encode} the
|
||||
* string bytes
|
||||
* @param srcIndex the starting index of the source string
|
||||
* @param numChars the number of characters to be copied
|
||||
* @return a new native segment containing the encoded string
|
||||
* @throws IndexOutOfBoundsException if either {@code srcIndex} or {@code numChars} are {@code < 0}
|
||||
* @throws IndexOutOfBoundsException if {@code srcIndex > str.length() - numChars}
|
||||
*
|
||||
* @implSpec The default implementation for this method copies the contents of the
|
||||
* provided Java string into a new memory segment obtained by calling
|
||||
* {@code this.allocate(B)}, where {@code B} is the size, in bytes, of
|
||||
* the string encoded using the provided charset
|
||||
* (e.g. {@code str.getBytes(charset).length});
|
||||
*/
|
||||
@ForceInline
|
||||
default MemorySegment allocateFrom(String str, Charset charset, int srcIndex, int numChars) {
|
||||
Objects.requireNonNull(charset);
|
||||
Objects.requireNonNull(str);
|
||||
Objects.checkFromIndexSize(srcIndex, numChars, str.length());
|
||||
MemorySegment segment;
|
||||
if (StringSupport.bytesCompatible(str, charset, srcIndex, numChars)) {
|
||||
segment = allocateNoInit(numChars);
|
||||
StringSupport.copyToSegmentRaw(str, segment, 0, srcIndex, numChars);
|
||||
} else {
|
||||
byte[] bytes = str.substring(srcIndex, srcIndex + numChars).getBytes(charset);
|
||||
segment = allocateNoInit(bytes.length);
|
||||
MemorySegment.copy(bytes, 0, segment, ValueLayout.JAVA_BYTE, 0, bytes.length);
|
||||
}
|
||||
return segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new memory segment initialized with the provided byte value}
|
||||
* <p>
|
||||
|
||||
@ -634,10 +634,10 @@ public interface JavaLangAccess {
|
||||
/**
|
||||
* Copy the string bytes to an existing segment, avoiding intermediate copies.
|
||||
*/
|
||||
void copyToSegmentRaw(String string, MemorySegment segment, long offset);
|
||||
void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength);
|
||||
|
||||
/**
|
||||
* Are the string bytes compatible with the given charset?
|
||||
*/
|
||||
boolean bytesCompatible(String string, Charset charset);
|
||||
boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars);
|
||||
}
|
||||
|
||||
@ -832,10 +832,8 @@ public abstract sealed class AbstractInstruction
|
||||
final int slot;
|
||||
final int constant;
|
||||
|
||||
public UnboundIncrementInstruction(int slot, int constant) {
|
||||
super(BytecodeHelpers.validateAndIsWideIinc(slot, constant)
|
||||
? Opcode.IINC_W
|
||||
: Opcode.IINC);
|
||||
public UnboundIncrementInstruction(Opcode op, int slot, int constant) {
|
||||
super(op);
|
||||
this.slot = slot;
|
||||
this.constant = constant;
|
||||
}
|
||||
|
||||
@ -450,6 +450,13 @@ public class BytecodeHelpers {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void validateIncrement(Opcode opcode, int slot, int constant) {
|
||||
if (validateAndIsWideIinc(slot, constant) && opcode != Opcode.IINC_W) {
|
||||
throw new IllegalArgumentException(
|
||||
"IINC: operands require wide encoding for %s".formatted(opcode));
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateRet(Opcode opcode, int slot) {
|
||||
if (opcode == Opcode.RET && (slot & ~0xFF) == 0 ||
|
||||
opcode == Opcode.RET_W && (slot & ~0xFFFF) == 0)
|
||||
|
||||
@ -551,6 +551,13 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
unsafeGetOffset() == that.unsafeGetOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(long offset, Charset charset, long byteLength) {
|
||||
Utils.checkNonNegativeArgument(byteLength, "byteLength");
|
||||
Objects.requireNonNull(charset);
|
||||
return StringSupport.read(this, offset, charset, byteLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
@ -702,6 +709,16 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) {
|
||||
Objects.requireNonNull(src);
|
||||
Objects.requireNonNull(dstEncoding);
|
||||
Objects.requireNonNull(dst);
|
||||
|
||||
AbstractMemorySegmentImpl destImpl = (AbstractMemorySegmentImpl)dst;
|
||||
return StringSupport.copyBytes(src, destImpl, dstEncoding, dstOffset, srcIndex, numChars);
|
||||
}
|
||||
|
||||
// accessors
|
||||
|
||||
@ForceInline
|
||||
|
||||
@ -30,11 +30,14 @@ import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.ScopedMemoryAccess;
|
||||
import jdk.internal.util.Architecture;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
import jdk.internal.util.Preconditions;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.*;
|
||||
|
||||
@ -58,6 +61,27 @@ public final class StringSupport {
|
||||
};
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static String read(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) {
|
||||
return readBytes(segment, offset, charset, length);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static String readBytes(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) {
|
||||
if (length > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Required length exceeds implementation limit");
|
||||
}
|
||||
final int lengthBytes = (int) length;
|
||||
final byte[] bytes = new byte[lengthBytes];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, lengthBytes);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static void write(AbstractMemorySegmentImpl segment, long offset, Charset charset, String string) {
|
||||
switch (CharsetKind.of(charset)) {
|
||||
@ -70,14 +94,7 @@ public final class StringSupport {
|
||||
@ForceInline
|
||||
private static String readByte(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
|
||||
final int len = strlenByte(segment, offset, segment.byteSize());
|
||||
final byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
return readBytes(segment, offset, charset, len);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
@ -89,14 +106,7 @@ public final class StringSupport {
|
||||
@ForceInline
|
||||
private static String readShort(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
|
||||
int len = strlenShort(segment, offset, segment.byteSize());
|
||||
byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
return readBytes(segment, offset, charset, len);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
@ -108,14 +118,7 @@ public final class StringSupport {
|
||||
@ForceInline
|
||||
private static String readInt(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
|
||||
int len = strlenInt(segment, offset, segment.byteSize());
|
||||
byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
return readBytes(segment, offset, charset, len);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
@ -345,22 +348,26 @@ public final class StringSupport {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean bytesCompatible(String string, Charset charset) {
|
||||
return JAVA_LANG_ACCESS.bytesCompatible(string, charset);
|
||||
public static boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) {
|
||||
return JAVA_LANG_ACCESS.bytesCompatible(string, charset, srcIndex, numChars);
|
||||
}
|
||||
|
||||
public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset) {
|
||||
if (bytesCompatible(string, charset)) {
|
||||
copyToSegmentRaw(string, segment, offset);
|
||||
return string.length();
|
||||
return copyBytes(string, segment, charset, offset, 0, string.length());
|
||||
}
|
||||
|
||||
public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset, int srcIndex, int numChars) {
|
||||
if (bytesCompatible(string, charset, srcIndex, numChars)) {
|
||||
copyToSegmentRaw(string, segment, offset, srcIndex, numChars);
|
||||
return numChars;
|
||||
} else {
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
byte[] bytes = string.substring(srcIndex, srcIndex + numChars).getBytes(charset);
|
||||
MemorySegment.copy(bytes, 0, segment, JAVA_BYTE, offset, bytes.length);
|
||||
return bytes.length;
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyToSegmentRaw(String string, MemorySegment segment, long offset) {
|
||||
JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset);
|
||||
public static void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) {
|
||||
JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset, srcIndex, srcLength);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -68,7 +68,7 @@ public class ECDSAOperations {
|
||||
|
||||
public ECDSAOperations(ECOperations ecOps, ECPoint basePoint) {
|
||||
this.ecOps = ecOps;
|
||||
this.basePoint = toAffinePoint(basePoint, ecOps.getField());
|
||||
this.basePoint = AffinePoint.fromECPoint(basePoint, ecOps.getField());
|
||||
}
|
||||
|
||||
public ECOperations getEcOperations() {
|
||||
@ -79,14 +79,6 @@ public class ECDSAOperations {
|
||||
return ecOps.multiply(basePoint, scalar).asAffine();
|
||||
}
|
||||
|
||||
public static AffinePoint toAffinePoint(ECPoint point,
|
||||
IntegerFieldModuloP field) {
|
||||
|
||||
ImmutableIntegerModuloP affineX = field.getElement(point.getAffineX());
|
||||
ImmutableIntegerModuloP affineY = field.getElement(point.getAffineY());
|
||||
return new AffinePoint(affineX, affineY);
|
||||
}
|
||||
|
||||
public static
|
||||
Optional<ECDSAOperations> forParameters(ECParameterSpec ecParams) {
|
||||
Optional<ECOperations> curveOps =
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -120,6 +120,15 @@ public interface TypeMirror extends AnnotatedConstruct {
|
||||
* The results of {@code t1.equals(t2)} and
|
||||
* {@code Types.isSameType(t1, t2)} may differ.
|
||||
*
|
||||
* @apiNote The identity of a {@code TypeMirror} involves implicit
|
||||
* state not directly accessible from its methods, including state
|
||||
* about the presence of unrelated types. {@code TypeMirror}
|
||||
* objects created by different implementations of these
|
||||
* interfaces should <i>not</i> be expected to compare as equal
|
||||
* even if "the same" type is being modeled; this is
|
||||
* analogous to the inequality of {@code Class} objects for the
|
||||
* same class file loaded through different class loaders.
|
||||
*
|
||||
* @param obj the object to be compared with this type
|
||||
* @return {@code true} if the specified object is equal to this one
|
||||
*/
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -106,6 +106,15 @@ public interface Types {
|
||||
* {@code TypeMirror} objects can have different annotations and
|
||||
* still be considered the same.
|
||||
*
|
||||
* @apiNote The identity of a {@code TypeMirror} involves implicit
|
||||
* state not directly accessible from its methods, including state
|
||||
* about the presence of unrelated types. {@code TypeMirror}
|
||||
* objects created by different implementations of these
|
||||
* interfaces should <i>not</i> be expected to compare as equal
|
||||
* even if "the same" type is being modeled; this is
|
||||
* analogous to the inequality of {@code Class} objects for the
|
||||
* same class file loaded through different class loaders.
|
||||
*
|
||||
* @param t1 the first type
|
||||
* @param t2 the second type
|
||||
* @return {@code true} if and only if the two types are the same
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -50,6 +50,7 @@ import jdk.jpackage.internal.model.LinuxLauncher;
|
||||
import jdk.jpackage.internal.model.LinuxPackage;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.util.CompositeProxy;
|
||||
import jdk.jpackage.internal.util.Enquoter;
|
||||
import jdk.jpackage.internal.util.PathUtils;
|
||||
import jdk.jpackage.internal.util.XmlUtils;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,13 +27,12 @@ package jdk.jpackage.internal;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -48,9 +47,6 @@ public final class LibProvidersLookup {
|
||||
return (new ToolValidator(TOOL_LDD).validate() == null);
|
||||
}
|
||||
|
||||
public LibProvidersLookup() {
|
||||
}
|
||||
|
||||
LibProvidersLookup setPackageLookup(PackageLookup v) {
|
||||
packageLookup = v;
|
||||
return this;
|
||||
@ -87,23 +83,20 @@ public final class LibProvidersLookup {
|
||||
}
|
||||
|
||||
private static List<Path> getNeededLibsForFile(Path path) throws IOException {
|
||||
List<Path> result = new ArrayList<>();
|
||||
int ret = Executor.of(TOOL_LDD, path.toString()).setOutputConsumer(lines -> {
|
||||
lines.map(line -> {
|
||||
Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return null;
|
||||
}).filter(Objects::nonNull).map(Path::of).forEach(result::add);
|
||||
}).execute();
|
||||
final var result = Executor.of(TOOL_LDD, path.toString()).saveOutput().execute();
|
||||
|
||||
if (ret != 0) {
|
||||
if (result.getExitCode() != 0) {
|
||||
// objdump failed. This is OK if the tool was applied to not a binary file
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return result;
|
||||
return result.stdout().stream().map(line -> {
|
||||
Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return null;
|
||||
}).filter(Objects::nonNull).map(Path::of).toList();
|
||||
}
|
||||
|
||||
private static Collection<Path> getNeededLibsForFiles(List<Path> paths) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -33,6 +33,7 @@ import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_R
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.cli.Options;
|
||||
import jdk.jpackage.internal.cli.StandardBundlingOperation;
|
||||
@ -44,19 +45,34 @@ import jdk.jpackage.internal.util.Result;
|
||||
public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
|
||||
public LinuxBundlingEnvironment() {
|
||||
super(build()
|
||||
.defaultOperation(() -> {
|
||||
return LazyLoad.SYS_ENV.value().map(LinuxSystemEnvironment::nativePackageType).map(DESCRIPTORS::get);
|
||||
})
|
||||
.bundler(CREATE_LINUX_APP_IMAGE, LinuxBundlingEnvironment::createAppImage)
|
||||
.bundler(CREATE_LINUX_DEB, LazyLoad::debSysEnv, LinuxBundlingEnvironment::createDebPackage)
|
||||
.bundler(CREATE_LINUX_RPM, LazyLoad::rpmSysEnv, LinuxBundlingEnvironment::createRpmPackage));
|
||||
super(build().mutate(builder -> {
|
||||
|
||||
// Wrap the generic Linux system environment supplier in the run-once wrapper
|
||||
// as this supplier is called from both RPM and DEB Linux system environment suppliers.
|
||||
var sysEnv = runOnce(() -> {
|
||||
return LinuxSystemEnvironment.create();
|
||||
});
|
||||
|
||||
Supplier<Result<LinuxDebSystemEnvironment>> debSysEnv = () -> {
|
||||
return LinuxDebSystemEnvironment.create(sysEnv.get());
|
||||
};
|
||||
|
||||
Supplier<Result<LinuxRpmSystemEnvironment>> rpmSysEnv = () -> {
|
||||
return LinuxRpmSystemEnvironment.create(sysEnv.get());
|
||||
};
|
||||
|
||||
builder.defaultOperation(() -> {
|
||||
return sysEnv.get().value().map(LinuxSystemEnvironment::nativePackageType).map(DESCRIPTORS::get);
|
||||
})
|
||||
.bundler(CREATE_LINUX_DEB, debSysEnv, LinuxBundlingEnvironment::createDebPackage)
|
||||
.bundler(CREATE_LINUX_RPM, rpmSysEnv, LinuxBundlingEnvironment::createRpmPackage);
|
||||
}).bundler(CREATE_LINUX_APP_IMAGE, LinuxBundlingEnvironment::createAppImage));
|
||||
}
|
||||
|
||||
private static void createDebPackage(Options options, LinuxDebSystemEnvironment sysEnv) {
|
||||
|
||||
createNativePackage(options,
|
||||
LinuxFromOptions.createLinuxDebPackage(options),
|
||||
LinuxFromOptions.createLinuxDebPackage(options, sysEnv),
|
||||
buildEnv()::create,
|
||||
LinuxBundlingEnvironment::buildPipeline,
|
||||
(env, pkg, outputDir) -> {
|
||||
@ -67,7 +83,7 @@ public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
private static void createRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) {
|
||||
|
||||
createNativePackage(options,
|
||||
LinuxFromOptions.createLinuxRpmPackage(options),
|
||||
LinuxFromOptions.createLinuxRpmPackage(options, sysEnv),
|
||||
buildEnv()::create,
|
||||
LinuxBundlingEnvironment::buildPipeline,
|
||||
(env, pkg, outputDir) -> {
|
||||
@ -90,23 +106,6 @@ public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
return new BuildEnvFromOptions().predefinedAppImageLayout(APPLICATION_LAYOUT);
|
||||
}
|
||||
|
||||
private static final class LazyLoad {
|
||||
|
||||
static Result<LinuxDebSystemEnvironment> debSysEnv() {
|
||||
return DEB_SYS_ENV;
|
||||
}
|
||||
|
||||
static Result<LinuxRpmSystemEnvironment> rpmSysEnv() {
|
||||
return RPM_SYS_ENV;
|
||||
}
|
||||
|
||||
private static final Result<LinuxSystemEnvironment> SYS_ENV = LinuxSystemEnvironment.create();
|
||||
|
||||
private static final Result<LinuxDebSystemEnvironment> DEB_SYS_ENV = LinuxDebSystemEnvironment.create(SYS_ENV);
|
||||
|
||||
private static final Result<LinuxRpmSystemEnvironment> RPM_SYS_ENV = LinuxRpmSystemEnvironment.create(SYS_ENV);
|
||||
}
|
||||
|
||||
private static final Map<PackageType, BundlingOperationDescriptor> DESCRIPTORS = Stream.of(
|
||||
CREATE_LINUX_DEB,
|
||||
CREATE_LINUX_RPM
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
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;
|
||||
@ -76,11 +75,11 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
|
||||
try {
|
||||
// Try the real path first as it works better on newer Ubuntu versions
|
||||
return findProvidingPackages(realPath, sysEnv.dpkg());
|
||||
return findProvidingPackages(realPath, sysEnv);
|
||||
} catch (IOException ex) {
|
||||
// Try the default path if differ
|
||||
if (!realPath.equals(file)) {
|
||||
return findProvidingPackages(file, sysEnv.dpkg());
|
||||
return findProvidingPackages(file, sysEnv);
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
@ -107,7 +106,7 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
|
||||
properties.forEach(property -> cmdline.add(property.name));
|
||||
|
||||
Map<String, String> actualValues = Executor.of(cmdline.toArray(String[]::new))
|
||||
Map<String, String> actualValues = Executor.of(cmdline)
|
||||
.saveOutput(true)
|
||||
.executeExpectSuccess()
|
||||
.getOutput().stream()
|
||||
@ -158,9 +157,8 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
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));
|
||||
Executor.of(cmdline).retryOnKnownErrorMessage(
|
||||
"semop(1): encountered an error: Invalid argument").execute();
|
||||
|
||||
Log.verbose(I18N.format("message.output-to-location", debFile.toAbsolutePath()));
|
||||
}
|
||||
@ -233,7 +231,7 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream<String> findProvidingPackages(Path file, Path dpkg) throws IOException {
|
||||
private static Stream<String> findProvidingPackages(Path file, LinuxDebSystemEnvironment sysEnv) 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.
|
||||
@ -279,9 +277,9 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
Set<String> archPackages = new HashSet<>();
|
||||
Set<String> otherPackages = new HashSet<>();
|
||||
|
||||
var debArch = LinuxPackageArch.getValue(LINUX_DEB);
|
||||
var debArch = sysEnv.packageArch().value();
|
||||
|
||||
Executor.of(dpkg.toString(), "-S", file.toString())
|
||||
Executor.of(sysEnv.dpkg().toString(), "-S", file.toString())
|
||||
.saveOutput(true).executeExpectSuccess()
|
||||
.getOutput().forEach(line -> {
|
||||
Matcher matcher = PACKAGE_NAME_REGEX.matcher(line);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,7 +28,7 @@ import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin;
|
||||
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxDebSystemEnvironment extends LinuxSystemEnvironment, LinuxDebSystemEnvironmentMixin {
|
||||
interface LinuxDebSystemEnvironment extends LinuxSystemEnvironment, LinuxDebSystemEnvironmentMixin {
|
||||
|
||||
static Result<LinuxDebSystemEnvironment> create(Result<LinuxSystemEnvironment> base) {
|
||||
return mixin(LinuxDebSystemEnvironment.class, base, LinuxDebSystemEnvironmentMixin::create);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,7 +29,7 @@ import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxDebSystemEnvironmentMixin {
|
||||
interface LinuxDebSystemEnvironmentMixin {
|
||||
Path dpkg();
|
||||
Path dpkgdeb();
|
||||
Path fakeroot();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -70,9 +70,9 @@ final class LinuxFromOptions {
|
||||
return LinuxApplication.create(appBuilder.create());
|
||||
}
|
||||
|
||||
static LinuxRpmPackage createLinuxRpmPackage(Options options) {
|
||||
static LinuxRpmPackage createLinuxRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) {
|
||||
|
||||
final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_RPM);
|
||||
final var superPkgBuilder = createLinuxPackageBuilder(options, sysEnv, LINUX_RPM);
|
||||
|
||||
final var pkgBuilder = new LinuxRpmPackageBuilder(superPkgBuilder);
|
||||
|
||||
@ -81,9 +81,9 @@ final class LinuxFromOptions {
|
||||
return pkgBuilder.create();
|
||||
}
|
||||
|
||||
static LinuxDebPackage createLinuxDebPackage(Options options) {
|
||||
static LinuxDebPackage createLinuxDebPackage(Options options, LinuxDebSystemEnvironment sysEnv) {
|
||||
|
||||
final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_DEB);
|
||||
final var superPkgBuilder = createLinuxPackageBuilder(options, sysEnv, LINUX_DEB);
|
||||
|
||||
final var pkgBuilder = new LinuxDebPackageBuilder(superPkgBuilder);
|
||||
|
||||
@ -99,7 +99,7 @@ final class LinuxFromOptions {
|
||||
return pkg;
|
||||
}
|
||||
|
||||
private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, StandardPackageType type) {
|
||||
private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, LinuxSystemEnvironment sysEnv, StandardPackageType type) {
|
||||
|
||||
final var app = createLinuxApplication(options);
|
||||
|
||||
@ -107,6 +107,8 @@ final class LinuxFromOptions {
|
||||
|
||||
final var pkgBuilder = new LinuxPackageBuilder(superPkgBuilder);
|
||||
|
||||
pkgBuilder.arch(sysEnv.packageArch());
|
||||
|
||||
LINUX_PACKAGE_DEPENDENCIES.ifPresentIn(options, pkgBuilder::additionalDependencies);
|
||||
LINUX_APP_CATEGORY.ifPresentIn(options, pkgBuilder::category);
|
||||
LINUX_MENU_GROUP.ifPresentIn(options, pkgBuilder::menuGroupName);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -32,6 +32,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import jdk.jpackage.internal.model.Launcher;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.util.Enquoter;
|
||||
|
||||
/**
|
||||
* Helper to install launchers as services using "systemd".
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,18 +25,20 @@
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier;
|
||||
import java.util.ArrayList;
|
||||
import jdk.jpackage.internal.model.StandardPackageType;
|
||||
import jdk.jpackage.internal.util.CommandOutputControl;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
final class LinuxPackageArch {
|
||||
record LinuxPackageArch(String value) {
|
||||
|
||||
static String getValue(StandardPackageType pkgType) {
|
||||
static Result<LinuxPackageArch> create(StandardPackageType pkgType) {
|
||||
switch (pkgType) {
|
||||
case LINUX_RPM -> {
|
||||
return RpmPackageArch.VALUE;
|
||||
return rpm().map(LinuxPackageArch::new);
|
||||
}
|
||||
case LINUX_DEB -> {
|
||||
return DebPackageArch.VALUE;
|
||||
return deb().map(LinuxPackageArch::new);
|
||||
}
|
||||
default -> {
|
||||
throw new IllegalArgumentException();
|
||||
@ -44,62 +46,51 @@ final class LinuxPackageArch {
|
||||
}
|
||||
}
|
||||
|
||||
private static class DebPackageArch {
|
||||
|
||||
static final String VALUE = toSupplier(DebPackageArch::getValue).get();
|
||||
|
||||
private static String getValue() throws IOException {
|
||||
return Executor.of("dpkg", "--print-architecture").saveOutput(true)
|
||||
.executeExpectSuccess().getOutput().get(0);
|
||||
}
|
||||
private static Result<String> deb() {
|
||||
var exec = Executor.of("dpkg", "--print-architecture").saveOutput(true);
|
||||
return Result.of(exec::executeExpectSuccess, IOException.class)
|
||||
.flatMap(LinuxPackageArch::getStdoutFirstLine);
|
||||
}
|
||||
|
||||
private static class RpmPackageArch {
|
||||
|
||||
/*
|
||||
* Various ways to get rpm arch. Needed to address JDK-8233143. rpmbuild is mandatory for
|
||||
* rpm packaging, try it first. rpm is optional and may not be available, use as the last
|
||||
* resort.
|
||||
*/
|
||||
private static enum RpmArchReader {
|
||||
Rpmbuild("rpmbuild", "--eval=%{_target_cpu}"),
|
||||
Rpm("rpm", "--eval=%{_target_cpu}");
|
||||
|
||||
RpmArchReader(String... cmdline) {
|
||||
this.cmdline = cmdline;
|
||||
private static Result<String> rpm() {
|
||||
var errors = new ArrayList<Exception>();
|
||||
for (var tool : RpmArchReader.values()) {
|
||||
var result = tool.getRpmArch();
|
||||
if (result.hasValue()) {
|
||||
return result;
|
||||
} else {
|
||||
errors.addAll(result.errors());
|
||||
}
|
||||
|
||||
String getRpmArch() throws IOException {
|
||||
Executor exec = Executor.of(cmdline).saveOutput(true);
|
||||
switch (this) {
|
||||
case Rpm -> {
|
||||
exec.executeExpectSuccess();
|
||||
}
|
||||
case Rpmbuild -> {
|
||||
if (exec.execute() != 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
return exec.getOutput().get(0);
|
||||
}
|
||||
|
||||
private final String[] cmdline;
|
||||
}
|
||||
|
||||
static final String VALUE = toSupplier(RpmPackageArch::getValue).get();
|
||||
return Result.ofErrors(errors);
|
||||
}
|
||||
|
||||
private static String getValue() throws IOException {
|
||||
for (var rpmArchReader : RpmArchReader.values()) {
|
||||
var rpmArchStr = rpmArchReader.getRpmArch();
|
||||
if (rpmArchStr != null) {
|
||||
return rpmArchStr;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("error.rpm-arch-not-detected");
|
||||
/*
|
||||
* Various ways to get rpm arch. Needed to address JDK-8233143. rpmbuild is mandatory for
|
||||
* rpm packaging, try it first. rpm is optional and may not be available, use as the last
|
||||
* resort.
|
||||
*/
|
||||
private enum RpmArchReader {
|
||||
RPMBUILD("rpmbuild", "--eval=%{_target_cpu}"),
|
||||
RPM("rpm", "--eval=%{_target_cpu}");
|
||||
|
||||
RpmArchReader(String... cmdline) {
|
||||
this.cmdline = cmdline;
|
||||
}
|
||||
|
||||
Result<String> getRpmArch() {
|
||||
var exec = Executor.of(cmdline).saveOutput(true);
|
||||
return Result.of(exec::executeExpectSuccess, IOException.class)
|
||||
.flatMap(LinuxPackageArch::getStdoutFirstLine);
|
||||
}
|
||||
|
||||
private final String[] cmdline;
|
||||
}
|
||||
|
||||
private static Result<String> getStdoutFirstLine(CommandOutputControl.Result result) {
|
||||
return Result.of(() -> {
|
||||
return result.stdout().stream().findFirst().orElseThrow(result::unexpected);
|
||||
}, IOException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -83,7 +83,7 @@ final class LinuxPackageBuilder {
|
||||
category(),
|
||||
Optional.ofNullable(additionalDependencies),
|
||||
release(),
|
||||
pkg.asStandardPackageType().map(LinuxPackageArch::getValue).orElseThrow()));
|
||||
arch.value()));
|
||||
}
|
||||
|
||||
LinuxPackageBuilder literalName(String v) {
|
||||
@ -119,6 +119,11 @@ final class LinuxPackageBuilder {
|
||||
return Optional.ofNullable(release);
|
||||
}
|
||||
|
||||
LinuxPackageBuilder arch(LinuxPackageArch v) {
|
||||
arch = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
private static LinuxApplicationLayout usrTreePackageLayout(Path prefix, String packageName) {
|
||||
final var lib = prefix.resolve(Path.of("lib", packageName));
|
||||
return LinuxApplicationLayout.create(
|
||||
@ -184,6 +189,7 @@ final class LinuxPackageBuilder {
|
||||
private String category;
|
||||
private String additionalDependencies;
|
||||
private String release;
|
||||
private LinuxPackageArch arch;
|
||||
|
||||
private final PackageBuilder pkgBuilder;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,7 +28,7 @@ import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin;
|
||||
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxRpmSystemEnvironment extends LinuxSystemEnvironment, LinuxRpmSystemEnvironmentMixin {
|
||||
interface LinuxRpmSystemEnvironment extends LinuxSystemEnvironment, LinuxRpmSystemEnvironmentMixin {
|
||||
|
||||
static Result<LinuxRpmSystemEnvironment> create(Result<LinuxSystemEnvironment> base) {
|
||||
return mixin(LinuxRpmSystemEnvironment.class, base, LinuxRpmSystemEnvironmentMixin::create);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -32,7 +32,7 @@ import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.model.DottedVersion;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxRpmSystemEnvironmentMixin {
|
||||
interface LinuxRpmSystemEnvironmentMixin {
|
||||
Path rpm();
|
||||
Path rpmbuild();
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,7 +27,6 @@ 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;
|
||||
@ -35,9 +34,10 @@ import jdk.jpackage.internal.model.StandardPackageType;
|
||||
import jdk.jpackage.internal.util.CompositeProxy;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxSystemEnvironment extends SystemEnvironment {
|
||||
interface LinuxSystemEnvironment extends SystemEnvironment {
|
||||
boolean soLookupAvailable();
|
||||
PackageType nativePackageType();
|
||||
LinuxPackageArch packageArch();
|
||||
|
||||
static Result<LinuxSystemEnvironment> create() {
|
||||
return detectNativePackageType().map(LinuxSystemEnvironment::create).orElseGet(() -> {
|
||||
@ -45,7 +45,7 @@ public interface LinuxSystemEnvironment extends SystemEnvironment {
|
||||
});
|
||||
}
|
||||
|
||||
static Optional<PackageType> detectNativePackageType() {
|
||||
static Optional<StandardPackageType> detectNativePackageType() {
|
||||
if (Internal.isDebian()) {
|
||||
return Optional.of(StandardPackageType.LINUX_DEB);
|
||||
} else if (Internal.isRpm()) {
|
||||
@ -55,13 +55,14 @@ public interface LinuxSystemEnvironment extends SystemEnvironment {
|
||||
}
|
||||
}
|
||||
|
||||
static Result<LinuxSystemEnvironment> create(PackageType nativePackageType) {
|
||||
return Result.ofValue(new Stub(LibProvidersLookup.supported(),
|
||||
Objects.requireNonNull(nativePackageType)));
|
||||
static Result<LinuxSystemEnvironment> create(StandardPackageType nativePackageType) {
|
||||
return LinuxPackageArch.create(nativePackageType).map(arch -> {
|
||||
return new Stub(LibProvidersLookup.supported(), nativePackageType, arch);
|
||||
});
|
||||
}
|
||||
|
||||
static <T, U extends LinuxSystemEnvironment> U createWithMixin(Class<U> type, LinuxSystemEnvironment base, T mixin) {
|
||||
return CompositeProxy.create(type, base, mixin);
|
||||
return CompositeProxy.build().invokeTunnel(CompositeProxyTunnel.INSTANCE).create(type, base, mixin);
|
||||
}
|
||||
|
||||
static <T, U extends LinuxSystemEnvironment> Result<U> mixin(Class<U> type,
|
||||
@ -79,7 +80,7 @@ public interface LinuxSystemEnvironment extends SystemEnvironment {
|
||||
}
|
||||
}
|
||||
|
||||
record Stub(boolean soLookupAvailable, PackageType nativePackageType) implements LinuxSystemEnvironment {
|
||||
record Stub(boolean soLookupAvailable, PackageType nativePackageType, LinuxPackageArch packageArch) implements LinuxSystemEnvironment {
|
||||
}
|
||||
|
||||
static final class Internal {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,8 +24,6 @@
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import static jdk.jpackage.internal.util.XmlUtils.initDocumentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -50,7 +48,7 @@ record AppImageInfoPListFile(String bundleIdentifier, String bundleName, String
|
||||
static AppImageInfoPListFile loadFromInfoPList(Path infoPListFile)
|
||||
throws IOException, InvalidPlistFileException, SAXException {
|
||||
|
||||
final var plistReader = new PListReader(initDocumentBuilder().parse(Files.newInputStream(infoPListFile)));
|
||||
final var plistReader = new PListReader(Files.readAllBytes(infoPListFile));
|
||||
|
||||
try {
|
||||
return new AppImageInfoPListFile(
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -48,6 +48,7 @@ import jdk.jpackage.internal.model.Launcher;
|
||||
import jdk.jpackage.internal.model.MacApplication;
|
||||
import jdk.jpackage.internal.model.RuntimeLayout;
|
||||
import jdk.jpackage.internal.util.PathUtils;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
import jdk.jpackage.internal.util.function.ExceptionBox;
|
||||
|
||||
|
||||
@ -188,11 +189,9 @@ final class AppImageSigner {
|
||||
}
|
||||
|
||||
private static boolean isXcodeDevToolsInstalled() {
|
||||
try {
|
||||
return Executor.of("/usr/bin/xcrun", "--help").setQuiet(true).execute() == 0;
|
||||
} catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
return Result.of(
|
||||
Executor.of("/usr/bin/xcrun", "--help").setQuiet(true)::executeExpectSuccess,
|
||||
IOException.class).hasValue();
|
||||
}
|
||||
|
||||
private static void unsign(Path path) throws IOException {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -34,7 +34,6 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
public final class Codesign {
|
||||
@ -94,14 +93,12 @@ public final class Codesign {
|
||||
|
||||
public void applyTo(Path path) throws IOException, CodesignException {
|
||||
|
||||
var exec = Executor.of(Stream.concat(
|
||||
cmdline.stream(),
|
||||
Stream.of(path.toString())).toArray(String[]::new)
|
||||
).saveOutput(true);
|
||||
var exec = Executor.of(cmdline).args(path.toString()).saveOutput(true);
|
||||
configureExecutor.ifPresent(configure -> configure.accept(exec));
|
||||
|
||||
if (exec.execute() != 0) {
|
||||
throw new CodesignException(exec.getOutput().toArray(String[]::new));
|
||||
var result = exec.execute();
|
||||
if (result.getExitCode() != 0) {
|
||||
throw new CodesignException(result.getOutput().toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -36,7 +36,6 @@ import java.util.Optional;
|
||||
import jdk.jpackage.internal.cli.Options;
|
||||
import jdk.jpackage.internal.model.MacPackage;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public class MacBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
|
||||
@ -45,7 +44,7 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
.defaultOperation(CREATE_MAC_DMG)
|
||||
.bundler(SIGN_MAC_APP_IMAGE, MacBundlingEnvironment::signAppImage)
|
||||
.bundler(CREATE_MAC_APP_IMAGE, MacBundlingEnvironment::createAppImage)
|
||||
.bundler(CREATE_MAC_DMG, LazyLoad::dmgSysEnv, MacBundlingEnvironment::createDmdPackage)
|
||||
.bundler(CREATE_MAC_DMG, MacDmgSystemEnvironment::create, MacBundlingEnvironment::createDmdPackage)
|
||||
.bundler(CREATE_MAC_PKG, MacBundlingEnvironment::createPkgPackage));
|
||||
}
|
||||
|
||||
@ -98,13 +97,4 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
.predefinedAppImageLayout(APPLICATION_LAYOUT)
|
||||
.predefinedRuntimeImageLayout(MacPackage::guessRuntimeLayout);
|
||||
}
|
||||
|
||||
private static final class LazyLoad {
|
||||
|
||||
static Result<MacDmgSystemEnvironment> dmgSysEnv() {
|
||||
return DMG_SYS_ENV;
|
||||
}
|
||||
|
||||
private static final Result<MacDmgSystemEnvironment> DMG_SYS_ENV = MacDmgSystemEnvironment.create();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -53,7 +53,7 @@ public final class MacCertificateUtils {
|
||||
keychain.map(Keychain::asCliArg).ifPresent(args::add);
|
||||
|
||||
return toSupplier(() -> {
|
||||
final var output = Executor.of(args.toArray(String[]::new))
|
||||
final var output = Executor.of(args)
|
||||
.setQuiet(true).saveOutput(true).executeExpectSuccess()
|
||||
.getOutput();
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -33,11 +33,15 @@ import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import jdk.jpackage.internal.PackagingPipeline.PackageTaskID;
|
||||
import jdk.jpackage.internal.PackagingPipeline.TaskID;
|
||||
import jdk.jpackage.internal.model.MacDmgPackage;
|
||||
@ -105,6 +109,10 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
return env.configDir().resolve(pkg.app().name() + "-license.plist");
|
||||
}
|
||||
|
||||
private Path finalDmg() {
|
||||
return outputDir.resolve(pkg.packageFileNameWithSuffix());
|
||||
}
|
||||
|
||||
Path protoDmg() {
|
||||
return dmgWorkdir().resolve("proto.dmg");
|
||||
}
|
||||
@ -128,6 +136,10 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
}
|
||||
}
|
||||
|
||||
private Executor hdiutil(String... args) {
|
||||
return Executor.of(sysEnv.hdiutil().toString()).args(args).storeOutputInFiles();
|
||||
}
|
||||
|
||||
private void prepareDMGSetupScript() throws IOException {
|
||||
Path dmgSetup = volumeScript();
|
||||
Log.verbose(MessageFormat.format(
|
||||
@ -211,13 +223,17 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
}
|
||||
}
|
||||
|
||||
private String hdiUtilVerbosityFlag() {
|
||||
return env.verbose() ? "-verbose" : "-quiet";
|
||||
}
|
||||
|
||||
private void buildDMG() throws IOException {
|
||||
boolean copyAppImage = false;
|
||||
|
||||
Path protoDMG = protoDmg();
|
||||
Path finalDMG = outputDir.resolve(pkg.packageFileNameWithSuffix());
|
||||
final Path protoDMG = protoDmg();
|
||||
final Path finalDMG = finalDmg();
|
||||
|
||||
Path srcFolder = env.appImageDir();
|
||||
final Path srcFolder = env.appImageDir();
|
||||
|
||||
Log.verbose(MessageFormat.format(I18N.getString(
|
||||
"message.creating-dmg-file"), finalDMG.toAbsolutePath()));
|
||||
@ -233,21 +249,17 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
Files.createDirectories(protoDMG.getParent());
|
||||
Files.createDirectories(finalDMG.getParent());
|
||||
|
||||
String hdiUtilVerbosityFlag = env.verbose() ?
|
||||
"-verbose" : "-quiet";
|
||||
final String hdiUtilVerbosityFlag = hdiUtilVerbosityFlag();
|
||||
|
||||
// create temp image
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
"create",
|
||||
hdiUtilVerbosityFlag,
|
||||
"-srcfolder", normalizedAbsolutePathString(srcFolder),
|
||||
"-volname", volumeName(),
|
||||
"-ov", normalizedAbsolutePathString(protoDMG),
|
||||
"-fs", "HFS+",
|
||||
"-format", "UDRW");
|
||||
try {
|
||||
IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT);
|
||||
hdiutil("create",
|
||||
hdiUtilVerbosityFlag,
|
||||
"-srcfolder", normalizedAbsolutePathString(srcFolder),
|
||||
"-volname", volumeName(),
|
||||
"-ov", normalizedAbsolutePathString(protoDMG),
|
||||
"-fs", "HFS+",
|
||||
"-format", "UDRW").executeExpectSuccess();
|
||||
} catch (IOException ex) {
|
||||
Log.verbose(ex); // Log exception
|
||||
|
||||
@ -260,31 +272,26 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
// not be bigger, but it will able to hold additional 50 megabytes of data.
|
||||
// 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(),
|
||||
"create",
|
||||
hdiUtilVerbosityFlag,
|
||||
"-size", String.valueOf(size),
|
||||
"-volname", volumeName(),
|
||||
"-ov", normalizedAbsolutePathString(protoDMG),
|
||||
"-fs", "HFS+");
|
||||
new RetryExecutor()
|
||||
.setMaxAttemptsCount(10)
|
||||
.setAttemptTimeoutMillis(3000)
|
||||
.setWriteOutputToFile(true)
|
||||
.execute(pb);
|
||||
hdiutil(
|
||||
"create",
|
||||
hdiUtilVerbosityFlag,
|
||||
"-size", String.valueOf(size),
|
||||
"-volname", volumeName(),
|
||||
"-ov", normalizedAbsolutePathString(protoDMG),
|
||||
"-fs", "HFS+"
|
||||
).retry()
|
||||
.setMaxAttemptsCount(10)
|
||||
.setAttemptTimeout(3, TimeUnit.SECONDS)
|
||||
.execute();
|
||||
}
|
||||
|
||||
final Path mountedVolume = volumePath();
|
||||
|
||||
// mount temp image
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
"attach",
|
||||
hdiutil("attach",
|
||||
normalizedAbsolutePathString(protoDMG),
|
||||
hdiUtilVerbosityFlag,
|
||||
"-mountroot", protoDMG.getParent().toString());
|
||||
IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT);
|
||||
|
||||
final Path mountedVolume = volumePath();
|
||||
"-mountroot", mountedVolume.getParent().toString()).executeExpectSuccess();
|
||||
|
||||
// Copy app image, since we did not create DMG with it, but instead we created
|
||||
// empty one.
|
||||
@ -302,9 +309,13 @@ 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(),
|
||||
normalizedAbsolutePathString(volumeScript()));
|
||||
IOUtils.exec(pb, 180); // Wait 3 minutes. See JDK-8248248.
|
||||
Executor.of(
|
||||
sysEnv.osascript().toString(),
|
||||
normalizedAbsolutePathString(volumeScript())
|
||||
)
|
||||
// Wait 3 minutes. See JDK-8248248.
|
||||
.timeout(3, TimeUnit.MINUTES)
|
||||
.executeExpectSuccess();
|
||||
} catch (IOException ex) {
|
||||
Log.verbose(ex);
|
||||
}
|
||||
@ -325,18 +336,18 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
// but it seems Finder excepts these bytes to be
|
||||
// "icnC" for the volume icon
|
||||
// (might not work on Mac 10.13 with old XCode)
|
||||
pb = new ProcessBuilder(
|
||||
Executor.of(
|
||||
sysEnv.setFileUtility().orElseThrow().toString(),
|
||||
"-c", "icnC",
|
||||
normalizedAbsolutePathString(volumeIconFile));
|
||||
IOUtils.exec(pb);
|
||||
normalizedAbsolutePathString(volumeIconFile)
|
||||
).executeExpectSuccess();
|
||||
volumeIconFile.toFile().setReadOnly();
|
||||
|
||||
pb = new ProcessBuilder(
|
||||
Executor.of(
|
||||
sysEnv.setFileUtility().orElseThrow().toString(),
|
||||
"-a", "C",
|
||||
normalizedAbsolutePathString(mountedVolume));
|
||||
IOUtils.exec(pb);
|
||||
normalizedAbsolutePathString(mountedVolume)
|
||||
).executeExpectSuccess();
|
||||
} catch (IOException ex) {
|
||||
Log.error(ex.getMessage());
|
||||
Log.verbose("Cannot enable custom icon using SetFile utility");
|
||||
@ -347,85 +358,23 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
|
||||
} finally {
|
||||
// Detach the temporary image
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
"detach",
|
||||
hdiUtilVerbosityFlag,
|
||||
normalizedAbsolutePathString(mountedVolume));
|
||||
// "hdiutil detach" might not work right away due to resource busy error, so
|
||||
// repeat detach several times.
|
||||
RetryExecutor retryExecutor = new RetryExecutor();
|
||||
// Image can get detach even if we got resource busy error, so stop
|
||||
// trying to detach it if it is no longer attached.
|
||||
retryExecutor.setExecutorInitializer(exec -> {
|
||||
if (!Files.exists(mountedVolume)) {
|
||||
retryExecutor.abort();
|
||||
}
|
||||
});
|
||||
try {
|
||||
// 10 times with 6 second delays.
|
||||
retryExecutor.setMaxAttemptsCount(10).setAttemptTimeoutMillis(6000)
|
||||
.execute(pb);
|
||||
} catch (IOException ex) {
|
||||
if (!retryExecutor.isAborted()) {
|
||||
// Now force to detach if it still attached
|
||||
if (Files.exists(mountedVolume)) {
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
"detach",
|
||||
"-force",
|
||||
hdiUtilVerbosityFlag,
|
||||
normalizedAbsolutePathString(mountedVolume));
|
||||
IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
detachVolume();
|
||||
}
|
||||
|
||||
// Compress it to a new image
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
"convert",
|
||||
normalizedAbsolutePathString(protoDMG),
|
||||
hdiUtilVerbosityFlag,
|
||||
"-format", "UDZO",
|
||||
"-o", normalizedAbsolutePathString(finalDMG));
|
||||
try {
|
||||
new RetryExecutor()
|
||||
.setMaxAttemptsCount(10)
|
||||
.setAttemptTimeoutMillis(3000)
|
||||
.execute(pb);
|
||||
} catch (Exception ex) {
|
||||
// Convert might failed if something holds file. Try to convert copy.
|
||||
Path protoCopyDMG = protoCopyDmg();
|
||||
Files.copy(protoDMG, protoCopyDMG);
|
||||
try {
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
"convert",
|
||||
normalizedAbsolutePathString(protoCopyDMG),
|
||||
hdiUtilVerbosityFlag,
|
||||
"-format", "UDZO",
|
||||
"-o", normalizedAbsolutePathString(finalDMG));
|
||||
IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT);
|
||||
} finally {
|
||||
Files.deleteIfExists(protoCopyDMG);
|
||||
}
|
||||
}
|
||||
convertProtoDmg();
|
||||
|
||||
//add license if needed
|
||||
if (pkg.licenseFile().isPresent()) {
|
||||
pb = new ProcessBuilder(
|
||||
sysEnv.hdiutil().toString(),
|
||||
hdiutil(
|
||||
"udifrez",
|
||||
normalizedAbsolutePathString(finalDMG),
|
||||
"-xml",
|
||||
normalizedAbsolutePathString(licenseFile())
|
||||
);
|
||||
new RetryExecutor()
|
||||
.setMaxAttemptsCount(10)
|
||||
.setAttemptTimeoutMillis(3000)
|
||||
.execute(pb);
|
||||
).retry()
|
||||
.setMaxAttemptsCount(10)
|
||||
.setAttemptTimeout(3, TimeUnit.SECONDS)
|
||||
.execute();
|
||||
}
|
||||
|
||||
try {
|
||||
@ -441,6 +390,69 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir,
|
||||
|
||||
}
|
||||
|
||||
private void detachVolume() throws IOException {
|
||||
var mountedVolume = volumePath();
|
||||
|
||||
// "hdiutil detach" might not work right away due to resource busy error, so
|
||||
// repeat detach several times.
|
||||
Globals.instance().objectFactory().<Void, IOException>retryExecutor(IOException.class).setExecutable(context -> {
|
||||
|
||||
List<String> cmdline = new ArrayList<>();
|
||||
cmdline.add("detach");
|
||||
|
||||
if (context.isLastAttempt()) {
|
||||
// The last attempt, force detach.
|
||||
cmdline.add("-force");
|
||||
}
|
||||
|
||||
cmdline.addAll(List.of(
|
||||
hdiUtilVerbosityFlag(),
|
||||
normalizedAbsolutePathString(mountedVolume)
|
||||
));
|
||||
|
||||
// The image can get detached even if we get a resource busy error,
|
||||
// so execute the detach command without checking the exit code.
|
||||
var result = hdiutil(cmdline.toArray(String[]::new)).execute();
|
||||
|
||||
if (result.getExitCode() == 0 || !Files.exists(mountedVolume)) {
|
||||
// Detached successfully!
|
||||
return null;
|
||||
} else {
|
||||
throw result.unexpected();
|
||||
}
|
||||
}).setMaxAttemptsCount(10).setAttemptTimeout(6, TimeUnit.SECONDS).execute();
|
||||
}
|
||||
|
||||
private void convertProtoDmg() throws IOException {
|
||||
|
||||
Function<Path, Executor> convert = srcDmg -> {
|
||||
return hdiutil(
|
||||
"convert",
|
||||
normalizedAbsolutePathString(srcDmg),
|
||||
hdiUtilVerbosityFlag(),
|
||||
"-format", "UDZO",
|
||||
"-o", normalizedAbsolutePathString(finalDmg()));
|
||||
};
|
||||
|
||||
// Convert it to a new image.
|
||||
try {
|
||||
convert.apply(protoDmg()).retry()
|
||||
.setMaxAttemptsCount(10)
|
||||
.setAttemptTimeout(3, TimeUnit.SECONDS)
|
||||
.execute();
|
||||
} catch (IOException ex) {
|
||||
Log.verbose(ex);
|
||||
// Something holds the file, try to convert a copy.
|
||||
Path copyDmg = protoCopyDmg();
|
||||
Files.copy(protoDmg(), copyDmg);
|
||||
try {
|
||||
convert.apply(copyDmg).executeExpectSuccess();
|
||||
} finally {
|
||||
Files.deleteIfExists(copyDmg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Background image name in resources
|
||||
private static final String DEFAULT_BACKGROUND_IMAGE = "background_dmg.tiff";
|
||||
private static final String DEFAULT_DMG_SETUP_SCRIPT = "DMGsetup.scpt";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,10 +25,11 @@
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
@ -54,41 +55,31 @@ record MacDmgSystemEnvironment(Path hdiutil, Path osascript, Optional<Path> setF
|
||||
// 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"};
|
||||
static Optional<Path> findSetFileUtility() {
|
||||
return SETFILE_KNOWN_PATHS.stream().filter(setFilePath -> {
|
||||
// Validate SetFile, if Xcode is not installed it will run, but exit with error code
|
||||
return Result.of(
|
||||
Executor.of(setFilePath.toString(), "-h").setQuiet(true)::executeExpectSuccess,
|
||||
IOException.class).hasValue();
|
||||
}).findFirst().or(() -> {
|
||||
// generic find attempt
|
||||
final var executor = Executor.of("/usr/bin/xcrun", "-find", "SetFile").setQuiet(true).saveFirstLineOfOutput();
|
||||
|
||||
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();
|
||||
return Result.of(executor::executeExpectSuccess, IOException.class).flatMap(execResult -> {
|
||||
return Result.of(() -> {
|
||||
return execResult.stdout().stream().findFirst().map(Path::of).orElseThrow(execResult::unexpected);
|
||||
}, Exception.class);
|
||||
}).value().filter(v -> {
|
||||
return new ToolValidator(v).checkExistsOnly().validate() == null;
|
||||
}).map(Path::toAbsolutePath);
|
||||
});
|
||||
}
|
||||
|
||||
static final List<Path> SETFILE_KNOWN_PATHS = Stream.of(
|
||||
"/Developer/Tools/SetFile",
|
||||
"/usr/bin/SetFile",
|
||||
"/Developer/usr/bin/SetFile").map(Path::of).collect(Collectors.toUnmodifiableList());
|
||||
|
||||
private static final Path HDIUTIL = Path.of("/usr/bin/hdiutil");
|
||||
private static final Path OSASCRIPT = Path.of("/usr/bin/osascript");
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -32,7 +32,6 @@ import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
@ -57,6 +56,7 @@ import jdk.jpackage.internal.PackagingPipeline.PackageTaskID;
|
||||
import jdk.jpackage.internal.PackagingPipeline.TaskID;
|
||||
import jdk.jpackage.internal.model.MacPkgPackage;
|
||||
import jdk.jpackage.internal.resources.ResourceLocator;
|
||||
import jdk.jpackage.internal.util.Enquoter;
|
||||
import jdk.jpackage.internal.util.XmlUtils;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
@ -108,7 +108,7 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional<Services> servic
|
||||
cmdline.addAll(allPkgbuildArgs());
|
||||
try {
|
||||
Files.createDirectories(path.getParent());
|
||||
IOUtils.exec(new ProcessBuilder(cmdline), false, null, true, Executor.INFINITE_TIMEOUT);
|
||||
Executor.of(cmdline).executeExpectSuccess();
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
@ -487,15 +487,13 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional<Services> servic
|
||||
|
||||
Files.createDirectories(cpl.getParent());
|
||||
|
||||
final var pb = new ProcessBuilder("/usr/bin/pkgbuild",
|
||||
Executor.of("/usr/bin/pkgbuild",
|
||||
"--root",
|
||||
normalizedAbsolutePathString(env.appImageDir()),
|
||||
"--install-location",
|
||||
normalizedAbsolutePathString(installLocation()),
|
||||
"--analyze",
|
||||
normalizedAbsolutePathString(cpl));
|
||||
|
||||
IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT);
|
||||
normalizedAbsolutePathString(cpl)).executeExpectSuccess();
|
||||
|
||||
patchCPLFile(cpl);
|
||||
}
|
||||
@ -544,8 +542,7 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional<Services> servic
|
||||
}
|
||||
commandLine.add(normalizedAbsolutePathString(finalPkg));
|
||||
|
||||
final var pb = new ProcessBuilder(commandLine);
|
||||
IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT);
|
||||
Executor.of(commandLine).executeExpectSuccess();
|
||||
}
|
||||
|
||||
private static Optional<Services> createServices(BuildEnv env, MacPkgPackage pkg) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,15 +27,17 @@ package jdk.jpackage.internal;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import jdk.internal.util.OSVersion;
|
||||
import jdk.jpackage.internal.util.function.ThrowingConsumer;
|
||||
|
||||
final class TempKeychain implements Closeable {
|
||||
|
||||
static void withKeychains(ThrowingConsumer<List<Keychain>, ? extends Exception> keychainConsumer, List<Keychain> keychains) throws Exception {
|
||||
static void withKeychains(Consumer<List<Keychain>> keychainConsumer, List<Keychain> keychains) {
|
||||
|
||||
keychains.forEach(Objects::requireNonNull);
|
||||
if (keychains.isEmpty() || OSVersion.current().compareTo(new OSVersion(10, 12)) < 0) {
|
||||
keychainConsumer.accept(keychains);
|
||||
@ -43,11 +45,14 @@ final class TempKeychain implements Closeable {
|
||||
// we need this for OS X 10.12+
|
||||
try (var tempKeychain = new TempKeychain(keychains)) {
|
||||
keychainConsumer.accept(tempKeychain.keychains);
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void withKeychain(ThrowingConsumer<Keychain, ? extends Exception> keychainConsumer, Keychain keychain) throws Exception {
|
||||
static void withKeychain(Consumer<Keychain> keychainConsumer, Keychain keychain) {
|
||||
|
||||
Objects.requireNonNull(keychainConsumer);
|
||||
withKeychains(keychains -> {
|
||||
keychainConsumer.accept(keychains.getFirst());
|
||||
@ -78,7 +83,7 @@ final class TempKeychain implements Closeable {
|
||||
|
||||
args.addAll(missingKeychains.stream().map(Keychain::asCliArg).toList());
|
||||
|
||||
Executor.of(args.toArray(String[]::new)).executeExpectSuccess();
|
||||
Executor.of(args).executeExpectSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +94,7 @@ final class TempKeychain implements Closeable {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (!restoreKeychainsCmd.isEmpty()) {
|
||||
Executor.of(restoreKeychainsCmd.toArray(String[]::new)).executeExpectSuccess();
|
||||
Executor.of(restoreKeychainsCmd).executeExpectSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,11 +28,12 @@ import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toUnmodifiableMap;
|
||||
import static java.util.stream.Collectors.toUnmodifiableSet;
|
||||
import static jdk.jpackage.internal.cli.StandardAppImageFileOption.APP_VERSION;
|
||||
import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_AS_SERVICE;
|
||||
import static jdk.jpackage.internal.cli.StandardAppImageFileOption.DESCRIPTION;
|
||||
import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_AS_SERVICE;
|
||||
import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_NAME;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
@ -162,7 +163,23 @@ final class AppImageFile {
|
||||
final var relativeAppImageFilePath = appImageDir.relativize(appImageFilePath);
|
||||
|
||||
try {
|
||||
final Document doc = XmlUtils.initDocumentBuilder().parse(Files.newInputStream(appImageFilePath));
|
||||
//
|
||||
// Use javax.xml.parsers.DocumentBuilder#parse(java.io.InputStream).
|
||||
// Don't use javax.xml.parsers.DocumentBuilder#parse(java.io.File) as this will introduce
|
||||
// dependency on how the XML parser reports filesystem I/O errors.
|
||||
// E.g.: the default JDK XML parser throws java.io.FileNotFoundException if the supplied
|
||||
// directory is not found and throws org.xml.sax.SAXParseException if the supplied file is a directory.
|
||||
// Another DOM XML parser (a different version of Xerces?) may behave differently.
|
||||
//
|
||||
// The use of javax.xml.parsers.DocumentBuilder#parse(java.io.InputStream) eliminates
|
||||
// differences in how XML parsers handle file system I/O errors.
|
||||
// Filesystem I/O is delegated to java.nio.file.Files#readAllBytes(java.nio.file.Path),
|
||||
// XML parser deals with the byte stream in memory and the error handling code
|
||||
// doesn't depend on how XML parser reports filesystem I/O errors because
|
||||
// it reads data from memory, not from the filesystem.
|
||||
//
|
||||
final Document doc = XmlUtils.initDocumentBuilder().parse(
|
||||
new ByteArrayInputStream(Files.readAllBytes(appImageFilePath)));
|
||||
|
||||
final XPath xPath = XPathFactory.newInstance().newXPath();
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -65,10 +65,10 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
|
||||
Map<BundlingOperationDescriptor, Supplier<Result<Consumer<Options>>>> bundlers) {
|
||||
|
||||
this.bundlers = bundlers.entrySet().stream().collect(toMap(Map.Entry::getKey, e -> {
|
||||
return new CachingSupplier<>(e.getValue());
|
||||
return runOnce(e.getValue());
|
||||
}));
|
||||
|
||||
this.defaultOperationSupplier = Objects.requireNonNull(defaultOperationSupplier).map(CachingSupplier::new);
|
||||
this.defaultOperationSupplier = Objects.requireNonNull(defaultOperationSupplier).map(DefaultBundlingEnvironment::runOnce);
|
||||
}
|
||||
|
||||
|
||||
@ -98,6 +98,11 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
|
||||
return bundler(op, () -> Result.ofValue(bundler));
|
||||
}
|
||||
|
||||
Builder mutate(Consumer<Builder> mutator) {
|
||||
mutator.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
private Supplier<Optional<BundlingOperationDescriptor>> defaultOperationSupplier;
|
||||
private final Map<BundlingOperationDescriptor, Supplier<Result<Consumer<Options>>>> bundlers = new HashMap<>();
|
||||
}
|
||||
@ -107,6 +112,10 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
static <T> Supplier<T> runOnce(Supplier<T> supplier) {
|
||||
return new CachingSupplier<>(supplier);
|
||||
}
|
||||
|
||||
static <T extends SystemEnvironment> Supplier<Result<Consumer<Options>>> createBundlerSupplier(
|
||||
Supplier<Result<T>> sysEnvResultSupplier, BiConsumer<Options, T> bundler) {
|
||||
Objects.requireNonNull(sysEnvResultSupplier);
|
||||
@ -279,5 +288,5 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
|
||||
|
||||
|
||||
private final Map<BundlingOperationDescriptor, Supplier<Result<Consumer<Options>>>> bundlers;
|
||||
private final Optional<CachingSupplier<Optional<BundlingOperationDescriptor>>> defaultOperationSupplier;
|
||||
private final Optional<Supplier<Optional<BundlingOperationDescriptor>>> defaultOperationSupplier;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,53 +25,153 @@
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.io.PrintStream;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.spi.ToolProvider;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.util.CommandLineFormat;
|
||||
import jdk.jpackage.internal.util.CommandOutputControl;
|
||||
import jdk.jpackage.internal.util.CommandOutputControl.ProcessAttributes;
|
||||
import jdk.jpackage.internal.util.CommandOutputControl.Result;
|
||||
import jdk.jpackage.internal.util.RetryExecutor;
|
||||
import jdk.jpackage.internal.util.function.ExceptionBox;
|
||||
|
||||
public final class Executor {
|
||||
final class Executor {
|
||||
|
||||
Executor() {
|
||||
static Executor of(String... cmdline) {
|
||||
return of(List.of(cmdline));
|
||||
}
|
||||
|
||||
Executor setOutputConsumer(Consumer<Stream<String>> v) {
|
||||
outputConsumer = v;
|
||||
return this;
|
||||
static Executor of(List<String> cmdline) {
|
||||
return of(new ProcessBuilder(cmdline));
|
||||
}
|
||||
|
||||
static Executor of(ProcessBuilder pb) {
|
||||
return Globals.instance().objectFactory().executor().processBuilder(pb);
|
||||
}
|
||||
|
||||
public Executor() {
|
||||
commandOutputControl = new CommandOutputControl();
|
||||
args = new ArrayList<>();
|
||||
}
|
||||
|
||||
private Executor(Executor other) {
|
||||
commandOutputControl = other.commandOutputControl.copy();
|
||||
quietCommand = other.quietCommand;
|
||||
args = new ArrayList<>(other.args);
|
||||
processBuilder = other.processBuilder;
|
||||
toolProvider = other.toolProvider;
|
||||
timeout = other.timeout;
|
||||
mapper = other.mapper;
|
||||
}
|
||||
|
||||
Executor saveOutput(boolean v) {
|
||||
saveOutput = v;
|
||||
commandOutputControl.saveOutput(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
Executor setWriteOutputToFile(boolean v) {
|
||||
writeOutputToFile = v;
|
||||
Executor saveOutput() {
|
||||
return saveOutput(true);
|
||||
}
|
||||
|
||||
Executor saveFirstLineOfOutput() {
|
||||
commandOutputControl.saveFirstLineOfOutput();
|
||||
return this;
|
||||
}
|
||||
|
||||
Executor setTimeout(long v) {
|
||||
Executor charset(Charset v) {
|
||||
commandOutputControl.charset(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
Executor storeOutputInFiles(boolean v) {
|
||||
commandOutputControl.storeOutputInFiles(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
Executor storeOutputInFiles() {
|
||||
return storeOutputInFiles(true);
|
||||
}
|
||||
|
||||
Executor binaryOutput(boolean v) {
|
||||
commandOutputControl.binaryOutput(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
Executor binaryOutput() {
|
||||
return binaryOutput(true);
|
||||
}
|
||||
|
||||
Executor discardStdout(boolean v) {
|
||||
commandOutputControl.discardStdout(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
Executor discardStdout() {
|
||||
return discardStdout(true);
|
||||
}
|
||||
|
||||
Executor discardStderr(boolean v) {
|
||||
commandOutputControl.discardStderr(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
Executor discardStderr() {
|
||||
return discardStderr(true);
|
||||
}
|
||||
|
||||
Executor timeout(long v, TimeUnit unit) {
|
||||
return timeout(Duration.of(v, unit.toChronoUnit()));
|
||||
}
|
||||
|
||||
Executor timeout(Duration v) {
|
||||
timeout = v;
|
||||
if (timeout != INFINITE_TIMEOUT) {
|
||||
// Redirect output to file if timeout is requested, otherwise we will
|
||||
// reading until process ends and timeout will never be reached.
|
||||
setWriteOutputToFile(true);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Executor setProcessBuilder(ProcessBuilder v) {
|
||||
pb = v;
|
||||
Executor toolProvider(ToolProvider v) {
|
||||
toolProvider = Objects.requireNonNull(v);
|
||||
processBuilder = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
Executor setCommandLine(String... cmdline) {
|
||||
return setProcessBuilder(new ProcessBuilder(cmdline));
|
||||
Optional<ToolProvider> toolProvider() {
|
||||
return Optional.ofNullable(toolProvider);
|
||||
}
|
||||
|
||||
Executor processBuilder(ProcessBuilder v) {
|
||||
processBuilder = Objects.requireNonNull(v);
|
||||
toolProvider = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
Optional<ProcessBuilder> processBuilder() {
|
||||
return Optional.ofNullable(processBuilder);
|
||||
}
|
||||
|
||||
Executor args(List<String> v) {
|
||||
args.addAll(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
Executor args(String... args) {
|
||||
return args(List.of(args));
|
||||
}
|
||||
|
||||
List<String> args() {
|
||||
return args;
|
||||
}
|
||||
|
||||
Executor setQuiet(boolean v) {
|
||||
@ -79,159 +179,207 @@ public final class Executor {
|
||||
return this;
|
||||
}
|
||||
|
||||
List<String> getOutput() {
|
||||
return output;
|
||||
}
|
||||
|
||||
Executor executeExpectSuccess() throws IOException {
|
||||
int ret = execute();
|
||||
if (0 != ret) {
|
||||
throw new IOException(
|
||||
String.format("Command %s exited with %d code",
|
||||
createLogMessage(pb, false), ret));
|
||||
}
|
||||
Executor mapper(UnaryOperator<Executor> v) {
|
||||
mapper = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
int execute() throws IOException {
|
||||
output = null;
|
||||
Optional<UnaryOperator<Executor>> mapper() {
|
||||
return Optional.ofNullable(mapper);
|
||||
}
|
||||
|
||||
boolean needProcessOutput = outputConsumer != null || Log.isVerbose() || saveOutput;
|
||||
Path outputFile = null;
|
||||
if (needProcessOutput) {
|
||||
pb.redirectErrorStream(true);
|
||||
if (writeOutputToFile) {
|
||||
outputFile = Files.createTempFile("jpackageOutputTempFile", ".tmp");
|
||||
pb.redirectOutput(outputFile.toFile());
|
||||
Executor copy() {
|
||||
return new Executor(this);
|
||||
}
|
||||
|
||||
Result execute() throws IOException {
|
||||
if (mapper != null) {
|
||||
var mappedExecutor = Objects.requireNonNull(mapper.apply(this));
|
||||
if (mappedExecutor != this) {
|
||||
return mappedExecutor.execute();
|
||||
}
|
||||
}
|
||||
|
||||
var coc = commandOutputControl.copy();
|
||||
|
||||
final CommandOutputControl.Executable exec;
|
||||
if (processBuilder != null) {
|
||||
exec = coc.createExecutable(copyProcessBuilder());
|
||||
} else if (toolProvider != null) {
|
||||
exec = coc.createExecutable(toolProvider, args.toArray(String[]::new));
|
||||
} else {
|
||||
// We are not going to read process output, so need to notify
|
||||
// ProcessBuilder about this. Otherwise some processes might just
|
||||
// hang up (`ldconfig -p`).
|
||||
pb.redirectError(ProcessBuilder.Redirect.DISCARD);
|
||||
pb.redirectOutput(ProcessBuilder.Redirect.DISCARD);
|
||||
throw new IllegalStateException("No target to execute");
|
||||
}
|
||||
|
||||
if (!quietCommand) {
|
||||
Log.verbose(String.format("Running %s", createLogMessage(pb, true)));
|
||||
PrintableOutputBuilder printableOutputBuilder;
|
||||
if (dumpOutput()) {
|
||||
printableOutputBuilder = new PrintableOutputBuilder(coc);
|
||||
} else {
|
||||
printableOutputBuilder = null;
|
||||
}
|
||||
|
||||
Process p = pb.start();
|
||||
|
||||
int code = 0;
|
||||
if (writeOutputToFile) {
|
||||
try {
|
||||
code = waitForProcess(p);
|
||||
} catch (InterruptedException ex) {
|
||||
Log.verbose(ex);
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (needProcessOutput) {
|
||||
final List<String> savedOutput;
|
||||
Supplier<Stream<String>> outputStream;
|
||||
|
||||
if (writeOutputToFile) {
|
||||
output = savedOutput = Files.readAllLines(outputFile);
|
||||
Files.delete(outputFile);
|
||||
outputStream = () -> {
|
||||
if (savedOutput != null) {
|
||||
return savedOutput.stream();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
if (outputConsumer != null) {
|
||||
outputConsumer.accept(outputStream.get());
|
||||
}
|
||||
} else {
|
||||
try (var br = new BufferedReader(new InputStreamReader(
|
||||
p.getInputStream()))) {
|
||||
|
||||
if ((outputConsumer != null || Log.isVerbose())
|
||||
|| saveOutput) {
|
||||
savedOutput = br.lines().toList();
|
||||
} else {
|
||||
savedOutput = null;
|
||||
}
|
||||
output = savedOutput;
|
||||
|
||||
outputStream = () -> {
|
||||
if (savedOutput != null) {
|
||||
return savedOutput.stream();
|
||||
}
|
||||
return br.lines();
|
||||
};
|
||||
if (outputConsumer != null) {
|
||||
outputConsumer.accept(outputStream.get());
|
||||
}
|
||||
|
||||
if (savedOutput == null) {
|
||||
// For some processes on Linux if the output stream
|
||||
// of the process is opened but not consumed, the process
|
||||
// would exit with code 141.
|
||||
// It turned out that reading just a single line of process
|
||||
// output fixes the problem, but let's process
|
||||
// all of the output, just in case.
|
||||
br.lines().forEach(x -> {});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dumpOutput()) {
|
||||
Log.verbose(String.format("Running %s", CommandLineFormat.DEFAULT.apply(List.of(commandLine().getFirst()))));
|
||||
}
|
||||
|
||||
Result result;
|
||||
try {
|
||||
if (!writeOutputToFile) {
|
||||
code = p.waitFor();
|
||||
}
|
||||
if (!quietCommand) {
|
||||
Log.verbose(pb.command(), getOutput(), code, IOUtils.getPID(p));
|
||||
}
|
||||
return code;
|
||||
} catch (InterruptedException ex) {
|
||||
Log.verbose(ex);
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private int waitForProcess(Process p) throws InterruptedException {
|
||||
if (timeout == INFINITE_TIMEOUT) {
|
||||
return p.waitFor();
|
||||
} else {
|
||||
if (p.waitFor(timeout, TimeUnit.SECONDS)) {
|
||||
return p.exitValue();
|
||||
if (timeout == null) {
|
||||
result = exec.execute();
|
||||
} else {
|
||||
Log.verbose(String.format("Command %s timeout after %d seconds",
|
||||
createLogMessage(pb, false), timeout));
|
||||
p.destroy();
|
||||
return -1;
|
||||
result = exec.execute(timeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
throw ExceptionBox.toUnchecked(ex);
|
||||
}
|
||||
|
||||
if (dumpOutput()) {
|
||||
log(result, printableOutputBuilder.create());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Result executeExpectSuccess() throws IOException {
|
||||
return execute().expectExitCode(0);
|
||||
}
|
||||
|
||||
Result executeExpect(int mainExitCode, int... otherExitCodes) throws IOException {
|
||||
return execute().expectExitCode(mainExitCode, otherExitCodes);
|
||||
}
|
||||
|
||||
RetryExecutor<Result, IOException> retry() {
|
||||
return Globals.instance().objectFactory().<Result, IOException>retryExecutor(IOException.class)
|
||||
.setExecutable(this::executeExpectSuccess);
|
||||
}
|
||||
|
||||
RetryExecutor<Result, IOException> retryOnKnownErrorMessage(String msg) {
|
||||
Objects.requireNonNull(msg);
|
||||
return saveOutput().retry().setExecutable(() -> {
|
||||
// Execute it without exit code check.
|
||||
var result = execute();
|
||||
if (result.stderr().stream().anyMatch(msg::equals)) {
|
||||
throw result.unexpected();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
List<String> commandLine() {
|
||||
if (processBuilder != null) {
|
||||
return Stream.of(processBuilder.command(), args).flatMap(Collection::stream).toList();
|
||||
} else if (toolProvider != null) {
|
||||
return Stream.concat(Stream.of(toolProvider.name()), args.stream()).toList();
|
||||
} else {
|
||||
throw new IllegalStateException("No target to execute");
|
||||
}
|
||||
}
|
||||
|
||||
private ProcessBuilder copyProcessBuilder() {
|
||||
if (processBuilder == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
var copy = new ProcessBuilder(commandLine());
|
||||
copy.directory(processBuilder.directory());
|
||||
var env = copy.environment();
|
||||
env.clear();
|
||||
env.putAll(processBuilder.environment());
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
private boolean dumpOutput() {
|
||||
return Log.isVerbose() && !quietCommand;
|
||||
}
|
||||
|
||||
private static void log(Result result, String printableOutput) throws IOException {
|
||||
Objects.requireNonNull(result);
|
||||
Objects.requireNonNull(printableOutput);
|
||||
|
||||
Optional<Long> pid;
|
||||
if (result.execAttrs() instanceof ProcessAttributes attrs) {
|
||||
pid = attrs.pid();
|
||||
} else {
|
||||
pid = Optional.empty();
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.append("Command");
|
||||
pid.ifPresent(p -> {
|
||||
sb.append(" [PID: ").append(p).append("]");
|
||||
});
|
||||
sb.append(":\n ").append(result.execAttrs());
|
||||
Log.verbose(sb.toString());
|
||||
|
||||
if (!printableOutput.isEmpty()) {
|
||||
sb.delete(0, sb.length());
|
||||
sb.append("Output:");
|
||||
try (var lines = new BufferedReader(new StringReader(printableOutput)).lines()) {
|
||||
lines.forEach(line -> {
|
||||
sb.append("\n ").append(line);
|
||||
});
|
||||
}
|
||||
Log.verbose(sb.toString());
|
||||
}
|
||||
|
||||
result.exitCode().ifPresentOrElse(exitCode -> {
|
||||
Log.verbose("Returned: " + exitCode + "\n");
|
||||
}, () -> {
|
||||
Log.verbose("Aborted: timed-out" + "\n");
|
||||
});
|
||||
}
|
||||
|
||||
private static final class PrintableOutputBuilder {
|
||||
|
||||
PrintableOutputBuilder(CommandOutputControl coc) {
|
||||
coc.dumpOutput(true);
|
||||
charset = coc.charset();
|
||||
if (coc.isBinaryOutput()) {
|
||||
// Assume binary output goes into stdout and text error messages go into stderr, so keep them separated.
|
||||
sinks = new ByteArrayOutputStream[2];
|
||||
sinks[0] = new ByteArrayOutputStream();
|
||||
sinks[1] = new ByteArrayOutputStream();
|
||||
coc.dumpStdout(new PrintStream(sinks[0], false, charset))
|
||||
.dumpStderr(new PrintStream(sinks[1], false, charset));
|
||||
} else {
|
||||
sinks = new ByteArrayOutputStream[1];
|
||||
sinks[0] = new ByteArrayOutputStream();
|
||||
var ps = new PrintStream(sinks[0], false, charset);
|
||||
// Redirect stderr in stdout.
|
||||
coc.dumpStdout(ps).dumpStderr(ps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Executor of(String... cmdline) {
|
||||
return new Executor().setCommandLine(cmdline);
|
||||
}
|
||||
|
||||
static Executor of(ProcessBuilder pb) {
|
||||
return new Executor().setProcessBuilder(pb);
|
||||
}
|
||||
|
||||
private static String createLogMessage(ProcessBuilder pb, boolean quiet) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append((quiet) ? pb.command().get(0) : pb.command());
|
||||
if (pb.directory() != null) {
|
||||
sb.append(String.format(" in %s", pb.directory().getAbsolutePath()));
|
||||
String create() {
|
||||
if (isBinaryOutput()) {
|
||||
// In case of binary output:
|
||||
// - Convert binary stdout to text using ISO-8859-1 encoding and
|
||||
// replace non-printable characters with the question mark symbol (?).
|
||||
// - Convert binary stderr to text using designated encoding (assume stderr is always a character stream).
|
||||
// - Merge text stdout and stderr into a single string;
|
||||
// stderr first, stdout follows, with the aim to present user error messages first.
|
||||
var sb = new StringBuilder();
|
||||
var stdout = sinks[0].toString(StandardCharsets.ISO_8859_1).replaceAll("[^\\p{Print}\\p{Space}]", "?");
|
||||
return sb.append(sinks[1].toString(charset)).append(stdout).toString();
|
||||
} else {
|
||||
return sinks[0].toString(charset);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
|
||||
private boolean isBinaryOutput() {
|
||||
return sinks.length == 2;
|
||||
}
|
||||
|
||||
private final ByteArrayOutputStream sinks[];
|
||||
private final Charset charset;
|
||||
}
|
||||
|
||||
public static final int INFINITE_TIMEOUT = -1;
|
||||
|
||||
private ProcessBuilder pb;
|
||||
private boolean saveOutput;
|
||||
private boolean writeOutputToFile;
|
||||
private final CommandOutputControl commandOutputControl;
|
||||
private boolean quietCommand;
|
||||
private long timeout = INFINITE_TIMEOUT;
|
||||
private List<String> output;
|
||||
private Consumer<Stream<String>> outputConsumer;
|
||||
private final List<String> args;
|
||||
private ProcessBuilder processBuilder;
|
||||
private ToolProvider toolProvider;
|
||||
private Duration timeout;
|
||||
private UnaryOperator<Executor> mapper;
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
@FunctionalInterface
|
||||
interface ExecutorFactory {
|
||||
|
||||
Executor executor();
|
||||
|
||||
static final ExecutorFactory DEFAULT = Executor::new;
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class Globals {
|
||||
|
||||
private Globals() {
|
||||
}
|
||||
|
||||
Globals objectFactory(ObjectFactory v) {
|
||||
checkMutable();
|
||||
objectFactory = Optional.ofNullable(v).orElse(ObjectFactory.DEFAULT);
|
||||
return this;
|
||||
}
|
||||
|
||||
ObjectFactory objectFactory() {
|
||||
return objectFactory;
|
||||
}
|
||||
|
||||
Globals executorFactory(ExecutorFactory v) {
|
||||
return objectFactory(ObjectFactory.build(objectFactory).executorFactory(v).create());
|
||||
}
|
||||
|
||||
public static int main(Supplier<Integer> mainBody) {
|
||||
if (INSTANCE.isBound()) {
|
||||
return mainBody.get();
|
||||
} else {
|
||||
return ScopedValue.where(INSTANCE, new Globals()).call(mainBody::get);
|
||||
}
|
||||
}
|
||||
|
||||
public static Globals instance() {
|
||||
return INSTANCE.orElse(DEFAULT);
|
||||
}
|
||||
|
||||
private void checkMutable() {
|
||||
if (this == DEFAULT) {
|
||||
throw new UnsupportedOperationException("Can't modify immutable instance");
|
||||
}
|
||||
}
|
||||
|
||||
private ObjectFactory objectFactory = ObjectFactory.DEFAULT;
|
||||
|
||||
private static final ScopedValue<Globals> INSTANCE = ScopedValue.newInstance();
|
||||
private static final Globals DEFAULT = new Globals();
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,12 +26,9 @@
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jdk.jpackage.internal.model.JPackageException;
|
||||
|
||||
/**
|
||||
@ -50,46 +47,6 @@ final class IOUtils {
|
||||
StandardCopyOption.COPY_ATTRIBUTES);
|
||||
}
|
||||
|
||||
public static void exec(ProcessBuilder pb)
|
||||
throws IOException {
|
||||
exec(pb, false, null, false, Executor.INFINITE_TIMEOUT);
|
||||
}
|
||||
|
||||
// timeout in seconds. -1 will be return if process timeouts.
|
||||
public static void exec(ProcessBuilder pb, long timeout)
|
||||
throws IOException {
|
||||
exec(pb, false, null, false, timeout);
|
||||
}
|
||||
|
||||
static void exec(ProcessBuilder pb, boolean testForPresenceOnly,
|
||||
PrintStream consumer, boolean writeOutputToFile, long timeout)
|
||||
throws IOException {
|
||||
exec(pb, testForPresenceOnly, consumer, writeOutputToFile,
|
||||
timeout, false);
|
||||
}
|
||||
|
||||
static void exec(ProcessBuilder pb, boolean testForPresenceOnly,
|
||||
PrintStream consumer, boolean writeOutputToFile,
|
||||
long timeout, boolean quiet) throws IOException {
|
||||
List<String> output = new ArrayList<>();
|
||||
Executor exec = Executor.of(pb)
|
||||
.setWriteOutputToFile(writeOutputToFile)
|
||||
.setTimeout(timeout)
|
||||
.setQuiet(quiet)
|
||||
.setOutputConsumer(lines -> {
|
||||
lines.forEach(output::add);
|
||||
if (consumer != null) {
|
||||
output.forEach(consumer::println);
|
||||
}
|
||||
});
|
||||
|
||||
if (testForPresenceOnly) {
|
||||
exec.execute();
|
||||
} else {
|
||||
exec.executeExpectSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
static void writableOutputDir(Path outdir) {
|
||||
if (!Files.isDirectory(outdir)) {
|
||||
try {
|
||||
@ -103,15 +60,4 @@ final class IOUtils {
|
||||
throw new JPackageException(I18N.format("error.cannot-write-to-output-dir", outdir.toAbsolutePath()));
|
||||
}
|
||||
}
|
||||
|
||||
public static long getPID(Process p) {
|
||||
try {
|
||||
return p.pid();
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
Log.verbose(ex); // Just log exception and ignore it. This method
|
||||
// is used for verbose output, so not a problem
|
||||
// if unsupported.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -22,13 +22,14 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import static jdk.jpackage.internal.model.RuntimeBuilder.getDefaultModulePath;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
@ -50,7 +51,6 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.module.ModulePath;
|
||||
import jdk.jpackage.internal.model.AppImageLayout;
|
||||
import jdk.jpackage.internal.model.JPackageException;
|
||||
import jdk.jpackage.internal.model.LauncherModularStartupInfo;
|
||||
import jdk.jpackage.internal.model.LauncherStartupInfo;
|
||||
import jdk.jpackage.internal.model.RuntimeBuilder;
|
||||
@ -58,27 +58,15 @@ import jdk.jpackage.internal.model.RuntimeBuilder;
|
||||
final class JLinkRuntimeBuilder implements RuntimeBuilder {
|
||||
|
||||
private JLinkRuntimeBuilder(List<String> jlinkCmdLine) {
|
||||
this.jlinkCmdLine = jlinkCmdLine;
|
||||
this.jlinkCmdLine = Objects.requireNonNull(jlinkCmdLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(AppImageLayout appImageLayout) {
|
||||
var args = new ArrayList<String>();
|
||||
args.add("--output");
|
||||
args.add(appImageLayout.runtimeDirectory().toString());
|
||||
args.addAll(jlinkCmdLine);
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(writer);
|
||||
|
||||
int retVal = LazyLoad.JLINK_TOOL.run(pw, pw, args.toArray(String[]::new));
|
||||
String jlinkOut = writer.toString();
|
||||
|
||||
args.add(0, "jlink");
|
||||
Log.verbose(args, List.of(jlinkOut), retVal, -1);
|
||||
if (retVal != 0) {
|
||||
throw new JPackageException(I18N.format("error.jlink.failed", jlinkOut));
|
||||
}
|
||||
toRunnable(Executor.of()
|
||||
.toolProvider(LazyLoad.JLINK_TOOL)
|
||||
.args("--output", appImageLayout.runtimeDirectory().toString())
|
||||
.args(jlinkCmdLine)::executeExpectSuccess).run();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,7 +28,6 @@ package jdk.jpackage.internal;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Log
|
||||
@ -105,29 +104,6 @@ public class Log {
|
||||
}
|
||||
}
|
||||
|
||||
public void verbose(List<String> strings,
|
||||
List<String> output, int returnCode, long pid) {
|
||||
if (verbose) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Command [PID: ");
|
||||
sb.append(pid);
|
||||
sb.append("]:\n ");
|
||||
|
||||
for (String s : strings) {
|
||||
sb.append(" " + s);
|
||||
}
|
||||
verbose(sb.toString());
|
||||
if (output != null && !output.isEmpty()) {
|
||||
sb = new StringBuilder("Output:");
|
||||
for (String s : output) {
|
||||
sb.append("\n " + s);
|
||||
}
|
||||
verbose(sb.toString());
|
||||
}
|
||||
verbose("Returned: " + returnCode + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
private String addTimestamp(String msg) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
|
||||
Date time = new Date(System.currentTimeMillis());
|
||||
@ -177,9 +153,4 @@ public class Log {
|
||||
public static void verbose(Throwable t) {
|
||||
instance.get().verbose(t);
|
||||
}
|
||||
|
||||
public static void verbose(List<String> strings, List<String> out,
|
||||
int ret, long pid) {
|
||||
instance.get().verbose(strings, out, ret, pid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import jdk.jpackage.internal.util.CompositeProxy;
|
||||
|
||||
interface ObjectFactory extends ExecutorFactory, RetryExecutorFactory {
|
||||
|
||||
static ObjectFactory.Builder build() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
static ObjectFactory.Builder build(ObjectFactory from) {
|
||||
return build().initFrom(from);
|
||||
}
|
||||
|
||||
static final class Builder {
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
ObjectFactory create() {
|
||||
return CompositeProxy.build().invokeTunnel(CompositeProxyTunnel.INSTANCE).create(
|
||||
ObjectFactory.class,
|
||||
Optional.ofNullable(executorFactory).orElse(ExecutorFactory.DEFAULT),
|
||||
Optional.ofNullable(retryExecutorFactory).orElse(RetryExecutorFactory.DEFAULT));
|
||||
}
|
||||
|
||||
Builder initFrom(ObjectFactory of) {
|
||||
Objects.requireNonNull(of);
|
||||
return executorFactory(of).retryExecutorFactory(of);
|
||||
}
|
||||
|
||||
Builder executorFactory(ExecutorFactory v) {
|
||||
executorFactory = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder retryExecutorFactory(RetryExecutorFactory v) {
|
||||
retryExecutorFactory = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
private ExecutorFactory executorFactory;
|
||||
private RetryExecutorFactory retryExecutorFactory;
|
||||
}
|
||||
|
||||
static final ObjectFactory DEFAULT = build().create();
|
||||
}
|
||||
@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2023, 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.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class RetryExecutor {
|
||||
public RetryExecutor() {
|
||||
setMaxAttemptsCount(5);
|
||||
setAttemptTimeoutMillis(2 * 1000);
|
||||
setWriteOutputToFile(false);
|
||||
}
|
||||
|
||||
public RetryExecutor setMaxAttemptsCount(int v) {
|
||||
attempts = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RetryExecutor setAttemptTimeoutMillis(int v) {
|
||||
timeoutMillis = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RetryExecutor saveOutput(boolean v) {
|
||||
saveOutput = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> getOutput() {
|
||||
return output;
|
||||
}
|
||||
|
||||
public RetryExecutor setWriteOutputToFile(boolean v) {
|
||||
writeOutputToFile = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RetryExecutor setExecutorInitializer(Consumer<Executor> v) {
|
||||
executorInitializer = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void abort() {
|
||||
aborted = true;
|
||||
}
|
||||
|
||||
public boolean isAborted() {
|
||||
return aborted;
|
||||
}
|
||||
|
||||
static RetryExecutor retryOnKnownErrorMessage(String v) {
|
||||
RetryExecutor result = new RetryExecutor();
|
||||
return result.setExecutorInitializer(exec -> {
|
||||
exec.setOutputConsumer(output -> {
|
||||
if (!output.anyMatch(v::equals)) {
|
||||
result.abort();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void execute(String cmdline[]) throws IOException {
|
||||
executeLoop(() ->
|
||||
Executor.of(cmdline).setWriteOutputToFile(writeOutputToFile));
|
||||
}
|
||||
|
||||
public void execute(ProcessBuilder pb) throws IOException {
|
||||
executeLoop(() ->
|
||||
Executor.of(pb).setWriteOutputToFile(writeOutputToFile));
|
||||
}
|
||||
|
||||
private void executeLoop(Supplier<Executor> execSupplier) throws IOException {
|
||||
aborted = false;
|
||||
for (;;) {
|
||||
if (aborted) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
Executor exec = execSupplier.get().saveOutput(saveOutput);
|
||||
if (executorInitializer != null) {
|
||||
executorInitializer.accept(exec);
|
||||
}
|
||||
exec.executeExpectSuccess();
|
||||
if (saveOutput) {
|
||||
output = exec.getOutput();
|
||||
}
|
||||
break;
|
||||
} catch (IOException ex) {
|
||||
if (aborted || (--attempts) <= 0) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(timeoutMillis);
|
||||
} catch (InterruptedException ex) {
|
||||
Log.verbose(ex);
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Consumer<Executor> executorInitializer;
|
||||
private boolean aborted;
|
||||
private int attempts;
|
||||
private int timeoutMillis;
|
||||
private boolean saveOutput;
|
||||
private List<String> output;
|
||||
private boolean writeOutputToFile;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import jdk.jpackage.internal.util.RetryExecutor;
|
||||
|
||||
@FunctionalInterface
|
||||
interface RetryExecutorFactory {
|
||||
|
||||
<T, E extends Exception> RetryExecutor<T, E> retryExecutor(Class<? extends E> exceptionType);
|
||||
|
||||
static final RetryExecutorFactory DEFAULT = RetryExecutor::new;
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,5 +24,5 @@
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
public interface SystemEnvironment {
|
||||
interface SystemEnvironment {
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -121,32 +121,32 @@ final class ToolValidator {
|
||||
cmdline.addAll(args);
|
||||
}
|
||||
|
||||
boolean canUseTool[] = new boolean[1];
|
||||
boolean canUseTool = false;
|
||||
if (minimalVersion == null) {
|
||||
// No version check.
|
||||
canUseTool[0] = true;
|
||||
canUseTool = true;
|
||||
}
|
||||
|
||||
String[] version = new String[1];
|
||||
String version = null;
|
||||
|
||||
try {
|
||||
Executor.of(cmdline.toArray(String[]::new)).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;
|
||||
}
|
||||
var result = Executor.of(cmdline).setQuiet(true).saveOutput().execute();
|
||||
var lines = result.content();
|
||||
if (versionParser != null && minimalVersion != null) {
|
||||
version = versionParser.apply(lines.stream());
|
||||
if (version != null && minimalVersion.compareTo(version) <= 0) {
|
||||
canUseTool = true;
|
||||
}
|
||||
}).execute();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return new ConfigException(I18N.format("error.tool-error", toolPath, e.getMessage()), null, e);
|
||||
}
|
||||
|
||||
if (canUseTool[0]) {
|
||||
if (canUseTool) {
|
||||
// All good. Tool can be used.
|
||||
return null;
|
||||
} else if (toolOldVersionErrorHandler != null) {
|
||||
return toolOldVersionErrorHandler.apply(toolPath, version[0]);
|
||||
return toolOldVersionErrorHandler.apply(toolPath, version);
|
||||
} else {
|
||||
return new ConfigException(
|
||||
I18N.format("error.tool-old-version", toolPath, minimalVersion),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -45,6 +45,7 @@ import java.util.function.Supplier;
|
||||
import java.util.spi.ToolProvider;
|
||||
import jdk.internal.opt.CommandLine;
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.internal.Globals;
|
||||
import jdk.jpackage.internal.Log;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.JPackageException;
|
||||
@ -56,7 +57,15 @@ import jdk.jpackage.internal.util.function.ExceptionBox;
|
||||
*/
|
||||
public final class Main {
|
||||
|
||||
public static final class Provider implements ToolProvider {
|
||||
public record Provider(Supplier<CliBundlingEnvironment> bundlingEnvSupplier) implements ToolProvider {
|
||||
|
||||
public Provider {
|
||||
Objects.requireNonNull(bundlingEnvSupplier);
|
||||
}
|
||||
|
||||
public Provider() {
|
||||
this(DefaultBundlingEnvironmentLoader.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
@ -65,7 +74,7 @@ public final class Main {
|
||||
|
||||
@Override
|
||||
public int run(PrintWriter out, PrintWriter err, String... args) {
|
||||
return Main.run(out, err, args);
|
||||
return Main.run(bundlingEnvSupplier, out, err, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -94,7 +103,23 @@ public final class Main {
|
||||
System.exit(run(out, err, args));
|
||||
}
|
||||
|
||||
public static int run(PrintWriter out, PrintWriter err, String... args) {
|
||||
static int run(PrintWriter out, PrintWriter err, String... args) {
|
||||
return run(DefaultBundlingEnvironmentLoader.INSTANCE, out, err, args);
|
||||
}
|
||||
|
||||
static int run(Supplier<CliBundlingEnvironment> bundlingEnvSupplier, PrintWriter out, PrintWriter err, String... args) {
|
||||
return Globals.main(() -> {
|
||||
return runWithGlobals(bundlingEnvSupplier, out, err, args);
|
||||
});
|
||||
}
|
||||
|
||||
private static int runWithGlobals(
|
||||
Supplier<CliBundlingEnvironment> bundlingEnvSupplier,
|
||||
PrintWriter out,
|
||||
PrintWriter err,
|
||||
String... args) {
|
||||
|
||||
Objects.requireNonNull(bundlingEnvSupplier);
|
||||
Objects.requireNonNull(args);
|
||||
for (String arg : args) {
|
||||
Objects.requireNonNull(arg);
|
||||
@ -128,8 +153,7 @@ public final class Main {
|
||||
return preprocessStatus;
|
||||
}
|
||||
|
||||
final var bundlingEnv = ServiceLoader.load(CliBundlingEnvironment.class,
|
||||
CliBundlingEnvironment.class.getClassLoader()).findFirst().orElseThrow();
|
||||
final var bundlingEnv = bundlingEnvSupplier.get();
|
||||
|
||||
final var parseResult = Utils.buildParser(OperatingSystem.current(), bundlingEnv).create().apply(mappedArgs.get());
|
||||
|
||||
@ -285,4 +309,15 @@ public final class Main {
|
||||
private static String getVersion() {
|
||||
return System.getProperty("java.version");
|
||||
}
|
||||
|
||||
private enum DefaultBundlingEnvironmentLoader implements Supplier<CliBundlingEnvironment> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public CliBundlingEnvironment get() {
|
||||
return ServiceLoader.load(
|
||||
CliBundlingEnvironment.class,
|
||||
CliBundlingEnvironment.class.getClassLoader()).findFirst().orElseThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -96,7 +96,6 @@ 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
|
||||
error.no.name.advice=Specify name with --name
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jpackage.internal.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Formats command line arguments.
|
||||
*/
|
||||
public final class CommandLineFormat {
|
||||
|
||||
public String format(List<String> cmdline) {
|
||||
return cmdline.stream().map(enquoter::applyTo).collect(Collectors.joining(" "));
|
||||
}
|
||||
|
||||
public static CommandLineFormat platform() {
|
||||
var format = new CommandLineFormat();
|
||||
format.enquoter = Enquoter.identity().setEnquotePredicate(Enquoter.QUOTE_IF_WHITESPACES).setQuoteChar('\'');
|
||||
return format;
|
||||
}
|
||||
|
||||
private CommandLineFormat() {
|
||||
}
|
||||
|
||||
private Enquoter enquoter;
|
||||
|
||||
public static final Function<List<String>, String> DEFAULT = platform()::format;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -22,7 +22,7 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
package jdk.jpackage.internal.util;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
@ -32,39 +32,43 @@ import java.util.regex.Pattern;
|
||||
/**
|
||||
* Add quotes to the given string in a configurable way.
|
||||
*/
|
||||
final class Enquoter {
|
||||
public final class Enquoter {
|
||||
|
||||
private Enquoter() {
|
||||
setQuoteChar('"');
|
||||
}
|
||||
|
||||
static Enquoter forPropertyValues() {
|
||||
public static Enquoter identity() {
|
||||
return new Enquoter();
|
||||
}
|
||||
|
||||
public static Enquoter forPropertyValues() {
|
||||
return new Enquoter()
|
||||
.setEnquotePredicate(QUOTE_IF_WHITESPACES)
|
||||
.setEscaper(PREPEND_BACKSLASH);
|
||||
}
|
||||
|
||||
static Enquoter forShellLiterals() {
|
||||
public static Enquoter forShellLiterals() {
|
||||
return forShellLiterals('\'');
|
||||
}
|
||||
|
||||
static Enquoter forShellLiterals(char quoteChar) {
|
||||
public static Enquoter forShellLiterals(char quoteChar) {
|
||||
return new Enquoter()
|
||||
.setQuoteChar(quoteChar)
|
||||
.setEnquotePredicate(x -> true)
|
||||
.setEscaper(PREPEND_BACKSLASH);
|
||||
}
|
||||
|
||||
String applyTo(String v) {
|
||||
public String applyTo(String v) {
|
||||
if (!needQuotes.test(v)) {
|
||||
return v;
|
||||
} else {
|
||||
var buf = new StringBuilder();
|
||||
buf.appendCodePoint(beginQuoteChr);
|
||||
Optional.of(escaper).ifPresentOrElse(op -> {
|
||||
Optional.ofNullable(escaper).ifPresentOrElse(op -> {
|
||||
v.codePoints().forEachOrdered(chr -> {
|
||||
if (chr == beginQuoteChr || chr == endQuoteChr) {
|
||||
escaper.accept(chr, buf);
|
||||
op.accept(chr, buf);
|
||||
} else {
|
||||
buf.appendCodePoint(chr);
|
||||
}
|
||||
@ -77,28 +81,23 @@ final class Enquoter {
|
||||
}
|
||||
}
|
||||
|
||||
Enquoter setQuoteChar(char chr) {
|
||||
public Enquoter setQuoteChar(char chr) {
|
||||
beginQuoteChr = chr;
|
||||
endQuoteChr = chr;
|
||||
return this;
|
||||
}
|
||||
|
||||
Enquoter setEscaper(BiConsumer<Integer, StringBuilder> v) {
|
||||
public Enquoter setEscaper(BiConsumer<Integer, StringBuilder> v) {
|
||||
escaper = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
Enquoter setEnquotePredicate(Predicate<String> v) {
|
||||
public Enquoter setEnquotePredicate(Predicate<String> v) {
|
||||
needQuotes = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
private int beginQuoteChr;
|
||||
private int endQuoteChr;
|
||||
private BiConsumer<Integer, StringBuilder> escaper;
|
||||
private Predicate<String> needQuotes = str -> false;
|
||||
|
||||
private static final Predicate<String> QUOTE_IF_WHITESPACES = new Predicate<String>() {
|
||||
public static final Predicate<String> QUOTE_IF_WHITESPACES = new Predicate<String>() {
|
||||
@Override
|
||||
public boolean test(String t) {
|
||||
return pattern.matcher(t).find();
|
||||
@ -106,8 +105,13 @@ final class Enquoter {
|
||||
private final Pattern pattern = Pattern.compile("\\s");
|
||||
};
|
||||
|
||||
private static final BiConsumer<Integer, StringBuilder> PREPEND_BACKSLASH = (chr, buf) -> {
|
||||
public static final BiConsumer<Integer, StringBuilder> PREPEND_BACKSLASH = (chr, buf) -> {
|
||||
buf.append('\\');
|
||||
buf.appendCodePoint(chr);
|
||||
};
|
||||
|
||||
private int beginQuoteChr;
|
||||
private int endQuoteChr;
|
||||
private BiConsumer<Integer, StringBuilder> escaper;
|
||||
private Predicate<String> needQuotes = str -> false;
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -35,7 +35,6 @@ import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
@ -311,7 +310,7 @@ public final class PListReader {
|
||||
}
|
||||
}
|
||||
|
||||
public PListReader(byte[] xmlData) throws ParserConfigurationException, SAXException, IOException {
|
||||
public PListReader(byte[] xmlData) throws SAXException, IOException {
|
||||
this(XmlUtils.initDocumentBuilder().parse(new ByteArrayInputStream(xmlData)));
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jpackage.internal.util;
|
||||
|
||||
import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import jdk.jpackage.internal.util.function.ExceptionBox;
|
||||
import jdk.jpackage.internal.util.function.ThrowingFunction;
|
||||
import jdk.jpackage.internal.util.function.ThrowingSupplier;
|
||||
|
||||
public class RetryExecutor<T, E extends Exception> {
|
||||
|
||||
public RetryExecutor(Class<? extends E> exceptionType) {
|
||||
this.exceptionType = Objects.requireNonNull(exceptionType);
|
||||
setMaxAttemptsCount(5);
|
||||
setAttemptTimeout(2, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
final public Class<? extends E> exceptionType() {
|
||||
return exceptionType;
|
||||
}
|
||||
|
||||
public RetryExecutor<T, E> setExecutable(ThrowingFunction<Context<RetryExecutor<T, E>>, T, E> v) {
|
||||
executable = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
final public RetryExecutor<T, E> setExecutable(ThrowingSupplier<T, E> v) {
|
||||
if (v != null) {
|
||||
setExecutable(_ -> {
|
||||
return v.get();
|
||||
});
|
||||
} else {
|
||||
executable = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public RetryExecutor<T, E> setMaxAttemptsCount(int v) {
|
||||
attempts = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
final public RetryExecutor<T, E> setAttemptTimeout(long v, TimeUnit unit) {
|
||||
return setAttemptTimeout(Duration.of(v, unit.toChronoUnit()));
|
||||
}
|
||||
|
||||
public RetryExecutor<T, E> setAttemptTimeout(Duration v) {
|
||||
timeout = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RetryExecutor<T, E> setExceptionMapper(Function<E, RuntimeException> v) {
|
||||
toUnchecked = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RetryExecutor<T, E> setSleepFunction(Consumer<Duration> v) {
|
||||
sleepFunction = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
final public RetryExecutor<T, E> mutate(Consumer<RetryExecutor<T, E>> mutator) {
|
||||
mutator.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public T execute() throws E {
|
||||
var curExecutable = executable();
|
||||
T result = null;
|
||||
var attemptIter = new DefaultContext();
|
||||
while (attemptIter.hasNext()) {
|
||||
attemptIter.next();
|
||||
try {
|
||||
result = curExecutable.apply(attemptIter);
|
||||
break;
|
||||
} catch (Exception ex) {
|
||||
if (!exceptionType.isInstance(ex)) {
|
||||
throw ExceptionBox.toUnchecked(ex);
|
||||
} else if (attemptIter.isLastAttempt()) {
|
||||
// No more attempts left. This is fatal.
|
||||
throw exceptionType.cast(ex);
|
||||
} else {
|
||||
curExecutable = executable();
|
||||
}
|
||||
}
|
||||
|
||||
sleep();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
final public T executeUnchecked() {
|
||||
try {
|
||||
return execute();
|
||||
} catch (Error | RuntimeException t) {
|
||||
throw t;
|
||||
} catch (Exception ex) {
|
||||
if (exceptionType.isInstance(ex)) {
|
||||
throw Optional.ofNullable(toUnchecked).orElse(ExceptionBox::toUnchecked).apply(exceptionType.cast(ex));
|
||||
} else {
|
||||
// Unreachable unless it is a direct subclass of Throwable,
|
||||
// which is not Error or Exception which should not happen.
|
||||
throw ExceptionBox.reachedUnreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface Context<T> {
|
||||
boolean isLastAttempt();
|
||||
int attempt();
|
||||
T executor();
|
||||
}
|
||||
|
||||
private final class DefaultContext implements Context<RetryExecutor<T, E>>, Iterator<Void> {
|
||||
|
||||
@Override
|
||||
public boolean isLastAttempt() {
|
||||
return !hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int attempt() {
|
||||
return attempt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return (attempts - attempt) > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void next() {
|
||||
attempt++;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RetryExecutor<T, E> executor() {
|
||||
return RetryExecutor.this;
|
||||
}
|
||||
|
||||
private int attempt = -1;
|
||||
}
|
||||
|
||||
private ThrowingFunction<Context<RetryExecutor<T, E>>, T, E> executable() {
|
||||
return Optional.ofNullable(executable).orElseThrow(() -> {
|
||||
return new IllegalStateException("No executable");
|
||||
});
|
||||
}
|
||||
|
||||
private void sleep() {
|
||||
Optional.ofNullable(timeout).ifPresent(Optional.ofNullable(sleepFunction).orElseGet(() -> {
|
||||
return toConsumer(Thread::sleep);
|
||||
}));
|
||||
}
|
||||
|
||||
private final Class<? extends E> exceptionType;
|
||||
private ThrowingFunction<Context<RetryExecutor<T, E>>, T, E> executable;
|
||||
private int attempts;
|
||||
private Duration timeout;
|
||||
private Function<E, RuntimeException> toUnchecked;
|
||||
private Consumer<Duration> sleepFunction;
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jpackage.internal.util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.Flushable;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Objects;
|
||||
import jdk.jpackage.internal.util.function.ThrowingConsumer;
|
||||
|
||||
public final class TeeOutputStream extends OutputStream {
|
||||
|
||||
public TeeOutputStream(Iterable<OutputStream> items) {
|
||||
items.forEach(Objects::requireNonNull);
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
for (final var item : items) {
|
||||
item.write(b);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
for (final var item : items) {
|
||||
item.write(b);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
for (final var item : items) {
|
||||
item.write(b, off, len);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
forEach(Flushable::flush);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
forEach(Closeable::close);
|
||||
}
|
||||
|
||||
private void forEach(ThrowingConsumer<OutputStream, IOException> c) throws IOException {
|
||||
IOException firstEx = null;
|
||||
for (final var item : items) {
|
||||
try {
|
||||
c.accept(item);
|
||||
} catch (IOException e) {
|
||||
if (firstEx == null) {
|
||||
firstEx = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (firstEx != null) {
|
||||
throw firstEx;
|
||||
}
|
||||
}
|
||||
|
||||
private final Iterable<OutputStream> items;
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,8 +24,6 @@
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import jdk.jpackage.internal.model.Launcher;
|
||||
import jdk.jpackage.internal.model.Application;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
@ -36,6 +34,9 @@ import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.model.Application;
|
||||
import jdk.jpackage.internal.model.Launcher;
|
||||
import jdk.jpackage.internal.util.Enquoter;
|
||||
|
||||
/**
|
||||
* Helper to install launchers as services for Unix installers.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,16 +31,17 @@ import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_EXE
|
||||
import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_MSI;
|
||||
|
||||
import jdk.jpackage.internal.cli.Options;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public class WinBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
|
||||
public WinBundlingEnvironment() {
|
||||
super(build()
|
||||
.defaultOperation(CREATE_WIN_EXE)
|
||||
.bundler(CREATE_WIN_APP_IMAGE, WinBundlingEnvironment::createAppImage)
|
||||
.bundler(CREATE_WIN_EXE, LazyLoad::sysEnv, WinBundlingEnvironment::createExePackage)
|
||||
.bundler(CREATE_WIN_MSI, LazyLoad::sysEnv, WinBundlingEnvironment::createMsiPackage));
|
||||
super(build().mutate(builder -> {
|
||||
var sysEnv = runOnce(WinSystemEnvironment::create);
|
||||
|
||||
builder
|
||||
.bundler(CREATE_WIN_EXE, sysEnv, WinBundlingEnvironment::createExePackage)
|
||||
.bundler(CREATE_WIN_MSI, sysEnv, WinBundlingEnvironment::createMsiPackage);
|
||||
}).defaultOperation(CREATE_WIN_EXE).bundler(CREATE_WIN_APP_IMAGE, WinBundlingEnvironment::createAppImage));
|
||||
}
|
||||
|
||||
private static void createMsiPackage(Options options, WinSystemEnvironment sysEnv) {
|
||||
@ -98,12 +99,4 @@ public class WinBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LazyLoad {
|
||||
|
||||
static Result<WinSystemEnvironment> sysEnv() {
|
||||
return SYS_ENV;
|
||||
}
|
||||
|
||||
private static final Result<WinSystemEnvironment> SYS_ENV = WinSystemEnvironment.create();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -233,10 +233,10 @@ public enum WixTool {
|
||||
// Detect FIPS mode
|
||||
var fips = false;
|
||||
try {
|
||||
final var exec = Executor.of(toolPath.toString(), "-?").setQuiet(true).saveOutput(true);
|
||||
final var exitCode = exec.execute();
|
||||
final var result = Executor.of(toolPath.toString(), "-?").setQuiet(true).saveOutput(true).execute();
|
||||
final var exitCode = result.getExitCode();
|
||||
if (exitCode != 0 /* 308 */) {
|
||||
final var output = exec.getOutput();
|
||||
final var output = result.getOutput();
|
||||
if (!output.isEmpty() && output.get(0).contains("error CNDL0308")) {
|
||||
fips = true;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,19 +23,50 @@
|
||||
|
||||
package jdk.test.failurehandler;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
public class HtmlPage implements AutoCloseable {
|
||||
static final String STYLE_SHEET_FILENAME = "failure-handler-style.css";
|
||||
static final String SCRIPT_FILENAME = "failure-handler-script.js";
|
||||
|
||||
private final PrintWriter writer;
|
||||
private final HtmlSection rootSection;
|
||||
|
||||
public HtmlPage(PrintWriter writer) {
|
||||
Objects.requireNonNull(writer, "writer cannot be null");
|
||||
this.writer = writer;
|
||||
/**
|
||||
* Constructs a {@code HtmlPage}
|
||||
*
|
||||
* @param dir The directory into which the HTML file and related resources will be created
|
||||
* @param htmlFileName The HTML file name
|
||||
* @param append if {@code true} then the content will be appended to the file represented
|
||||
* by the {@code htmlFileName}, else the {@code htmlFileName} will be overwritten
|
||||
* with the new content
|
||||
* @throws IllegalArgumentException if {@code dir} is not a directory or if the
|
||||
* {@code htmlFileName} is {@linkplain String#isBlank() blank}
|
||||
* @throws IOException if there is an error constructing file resource(s) for this HTML page
|
||||
*/
|
||||
public HtmlPage(final Path dir, final String htmlFileName, final boolean append)
|
||||
throws IOException {
|
||||
Objects.requireNonNull(dir, "directory cannot be null");
|
||||
Objects.requireNonNull(htmlFileName, "HTML file name cannot be null");
|
||||
if (!Files.isDirectory(dir)) {
|
||||
throw new IllegalArgumentException(dir + " is not a directory");
|
||||
}
|
||||
if (htmlFileName.isBlank()) {
|
||||
throw new IllegalArgumentException("HTML file name cannot be blank");
|
||||
}
|
||||
final FileWriter fileWriter = new FileWriter(dir.resolve(htmlFileName).toFile(), append);
|
||||
this.writer = new PrintWriter(fileWriter, true);
|
||||
createScriptFile(dir);
|
||||
createStyleSheetFile(dir);
|
||||
rootSection = new HtmlSection(writer);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
writer.close();
|
||||
@ -44,4 +75,71 @@ public class HtmlPage implements AutoCloseable {
|
||||
public HtmlSection getRootSection() {
|
||||
return rootSection;
|
||||
}
|
||||
|
||||
private static void createStyleSheetFile(final Path destDir) throws IOException {
|
||||
final Path styleSheet = destDir.resolve(STYLE_SHEET_FILENAME);
|
||||
if (Files.exists(styleSheet)) {
|
||||
return;
|
||||
}
|
||||
final String content = """
|
||||
div { display:none;}
|
||||
""";
|
||||
Files.writeString(styleSheet, content);
|
||||
}
|
||||
|
||||
private static void createScriptFile(final Path destDir) throws IOException {
|
||||
final Path script = destDir.resolve(SCRIPT_FILENAME);
|
||||
if (Files.exists(script)) {
|
||||
return;
|
||||
}
|
||||
final String content = """
|
||||
function doShow(e) {
|
||||
while (e != null) {
|
||||
if (e.tagName == 'DIV') {
|
||||
e.style.display = 'block';
|
||||
}
|
||||
e = e.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
function showHandler(event) {
|
||||
elementId = this.dataset.show;
|
||||
elementToShow = document.getElementById(elementId);
|
||||
doShow(elementToShow);
|
||||
}
|
||||
|
||||
function toggleHandler(event) {
|
||||
toggleElementId = this.dataset.toggle;
|
||||
elementToToggle = document.getElementById(toggleElementId);
|
||||
d = elementToToggle.style.display;
|
||||
if (d == 'block') {
|
||||
elementToToggle.style.display = 'none';
|
||||
} else {
|
||||
doShow(elementToToggle);
|
||||
}
|
||||
}
|
||||
|
||||
function bodyLoadHandler() {
|
||||
const index = location.href.indexOf("#");
|
||||
if (index != -1) {
|
||||
doShow(document.getElementById(location.href.substring(index + 1)));
|
||||
}
|
||||
// elements that require the "toggleHandler" function to be registered
|
||||
// as an event handler for the onclick event
|
||||
const requiringToggleHandler = document.querySelectorAll("[data-toggle]");
|
||||
for (const e of requiringToggleHandler) {
|
||||
e.addEventListener("click", toggleHandler);
|
||||
}
|
||||
// elements that require the "showHandler" function to be registered
|
||||
// as an event handler for the onclick event
|
||||
const requiringShowHandler = document.querySelectorAll("[data-show]");
|
||||
for (const e of requiringShowHandler) {
|
||||
e.addEventListener("click", showHandler);
|
||||
}
|
||||
}
|
||||
// register a onload event handler
|
||||
window.addEventListener("DOMContentLoaded", bodyLoadHandler);
|
||||
""";
|
||||
Files.writeString(script, content);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -57,41 +57,15 @@ public class HtmlSection {
|
||||
if (rootSection == null) {
|
||||
this.rootSection = this;
|
||||
this.pw.println("<html>");
|
||||
this.pw.println("<style>\n"
|
||||
+ "div { display:none;}\n"
|
||||
+ "</style>\n"
|
||||
+ "\n"
|
||||
+ "<script>\n"
|
||||
+ "function show(e) {\n"
|
||||
+ " while (e != null) {\n"
|
||||
+ " if (e.tagName == 'DIV') {\n"
|
||||
+ " e.style.display = 'block';\n"
|
||||
+ " }\n"
|
||||
+ " e = e.parentNode;\n"
|
||||
+ " }\n"
|
||||
+ "}\n"
|
||||
+ "\n"
|
||||
+ "function toggle(id) {\n"
|
||||
+ " e = document.getElementById(id);\n"
|
||||
+ " d = e.style.display;\n"
|
||||
+ " if (d == 'block') {\n"
|
||||
+ " e.style.display = 'none';\n"
|
||||
+ " } else {\n"
|
||||
+ " show(e);\n"
|
||||
+ " }\n"
|
||||
+ "}\n"
|
||||
+ "\n"
|
||||
+ "function main() {\n"
|
||||
+ " index = location.href.indexOf(\"#\");"
|
||||
+ " if (index != -1) {\n"
|
||||
+ " show(document.getElementById(location.href.substring(index + 1)));\n"
|
||||
+ " }\n"
|
||||
+ "}\n"
|
||||
+ "\n"
|
||||
+ "</script>\n"
|
||||
+ "</head>");
|
||||
|
||||
this.pw.println("<body onload='main()'>");
|
||||
this.pw.println("<head>");
|
||||
this.pw.println(
|
||||
"<link href=\"" + HtmlPage.STYLE_SHEET_FILENAME + "\" rel=\"stylesheet\" type=\"text/css\" />");
|
||||
this.pw.println(
|
||||
"<script src=\"" + HtmlPage.SCRIPT_FILENAME + "\" type=\"text/javascript\" ></script>");
|
||||
this.pw.println("</head>");
|
||||
|
||||
this.pw.println("<body>");
|
||||
} else {
|
||||
this.rootSection = rootSection;
|
||||
this.pw.print("<ul>");
|
||||
@ -146,7 +120,7 @@ public class HtmlSection {
|
||||
} else if (child != null) {
|
||||
path = String.format("%s.%s", path, child);
|
||||
}
|
||||
pw.printf("<a href=\"#%1$s\" onclick=\"show(document.getElementById('%1$s')); return true;\">%2$s</a>%n",
|
||||
pw.printf("<a href=\"#%1$s\" data-show=\"%1$s\" >%2$s</a>%n",
|
||||
path, name);
|
||||
}
|
||||
|
||||
@ -188,7 +162,7 @@ public class HtmlSection {
|
||||
: String.format("%s.%s", parent.id, name),
|
||||
name, rootSection);
|
||||
this.parent = parent;
|
||||
pw.printf("<li><a name='%1$s'/><a href='#%1$s' onclick=\"toggle('%1$s'); return false;\">%2$s</a><div id='%1$s'><code><pre>",
|
||||
pw.printf("<li><a name='%1$s'/><a href='#%1$s' data-toggle=\"%1$s\" >%2$s</a><div id='%1$s'><code><pre>",
|
||||
id, name);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -105,9 +105,7 @@ public class GatherDiagnosticInfoObserver implements Harness.Observer {
|
||||
|
||||
private void gatherCoreInfo(Path workDir, String name, Path core, PrintWriter log,
|
||||
CoreInfoGatherer gatherer) {
|
||||
File output = workDir.resolve(CORES_OUTPUT).toFile();
|
||||
try (HtmlPage html = new HtmlPage(new PrintWriter(
|
||||
new FileWriter(output, true), true))) {
|
||||
try (HtmlPage html = new HtmlPage(workDir, CORES_OUTPUT, true)) {
|
||||
try (ElapsedTimePrinter timePrinter
|
||||
= new ElapsedTimePrinter(new Stopwatch(), name, log)) {
|
||||
gatherer.gatherCoreInfo(html.getRootSection(), core);
|
||||
@ -121,9 +119,7 @@ public class GatherDiagnosticInfoObserver implements Harness.Observer {
|
||||
|
||||
private void gatherEnvInfo(Path workDir, String name, PrintWriter log,
|
||||
EnvironmentInfoGatherer gatherer) {
|
||||
File output = workDir.resolve(ENVIRONMENT_OUTPUT).toFile();
|
||||
try (HtmlPage html = new HtmlPage(new PrintWriter(
|
||||
new FileWriter(output, true), true))) {
|
||||
try (HtmlPage html = new HtmlPage(workDir, ENVIRONMENT_OUTPUT, true)) {
|
||||
try (ElapsedTimePrinter timePrinter
|
||||
= new ElapsedTimePrinter(new Stopwatch(), name, log)) {
|
||||
gatherer.gatherEnvironmentInfo(html.getRootSection());
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -69,16 +69,7 @@ public class GatherProcessInfoTimeoutHandler extends TimeoutHandler {
|
||||
}
|
||||
try {
|
||||
actionsLog.printf("%s ---%n", name);
|
||||
|
||||
File output = workDir.resolve(OUTPUT_FILENAME).toFile();
|
||||
try {
|
||||
PrintWriter pw = new PrintWriter(new FileWriter(output, true), true);
|
||||
runGatherer(name, workDir, actionsLog, pw, pid);
|
||||
} catch (IOException e) {
|
||||
actionsLog.printf("IOException: cannot open output file[%s] : %s",
|
||||
output, e.getMessage());
|
||||
e.printStackTrace(actionsLog);
|
||||
}
|
||||
runGatherer(name, actionsLog, pid);
|
||||
} finally {
|
||||
actionsLog.printf("--- %s%n", name);
|
||||
// don't close jtreg log
|
||||
@ -90,9 +81,9 @@ public class GatherProcessInfoTimeoutHandler extends TimeoutHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void runGatherer(String name, Path workDir, PrintWriter log,
|
||||
PrintWriter out, long pid) {
|
||||
try (HtmlPage html = new HtmlPage(out)) {
|
||||
private void runGatherer(String name, PrintWriter log, long pid) {
|
||||
Path workDir = outputDir.toPath();
|
||||
try (HtmlPage html = new HtmlPage(workDir, OUTPUT_FILENAME, true)) {
|
||||
ProcessInfoGatherer gatherer = new GathererFactory(
|
||||
OS.current().family,
|
||||
workDir, log, testJdk.toPath()).getProcessInfoGatherer();
|
||||
|
||||
@ -162,6 +162,35 @@ TEST_VM(AtomicIntegerTest, cmpxchg_int64) {
|
||||
Support().test();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct AtomicIntegerCmpsetTestSupport {
|
||||
Atomic<T> _test_value;
|
||||
|
||||
AtomicIntegerCmpsetTestSupport() : _test_value{} {}
|
||||
|
||||
void test() {
|
||||
T zero = 0;
|
||||
T five = 5;
|
||||
T ten = 10;
|
||||
_test_value.store_relaxed(zero);
|
||||
EXPECT_FALSE(_test_value.compare_set(five, ten));
|
||||
EXPECT_EQ(zero, _test_value.load_relaxed());
|
||||
EXPECT_TRUE(_test_value.compare_set(zero, ten));
|
||||
EXPECT_EQ(ten, _test_value.load_relaxed());
|
||||
}
|
||||
};
|
||||
|
||||
TEST_VM(AtomicIntegerTest, cmpset_int32) {
|
||||
using Support = AtomicIntegerCmpsetTestSupport<int32_t>;
|
||||
Support().test();
|
||||
}
|
||||
|
||||
TEST_VM(AtomicIntegerTest, cmpset_int64) {
|
||||
// Check if 64-bit atomics are available on the machine.
|
||||
using Support = AtomicIntegerCmpsetTestSupport<int64_t>;
|
||||
Support().test();
|
||||
}
|
||||
|
||||
struct AtomicXchgAndCmpxchg1ByteStressSupport {
|
||||
char _default_val;
|
||||
int _base;
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 compiler.c2.igvn;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8372302
|
||||
* @summary ModINode::Ideal and ModLNode::Ideal use an intermediate "hook" node
|
||||
* to keep stuff alive between phase->transform(...) calls. In some cases,
|
||||
* this node is not properly deleted before returning, causing failure
|
||||
* in the verification because the node count has changed. This test
|
||||
* ensures that the intermediate node gets destroyed before returning.
|
||||
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
|
||||
* -Xcomp -XX:-TieredCompilation
|
||||
* -XX:CompileCommand=compileonly,${test.main.class}::test*
|
||||
* -XX:VerifyIterativeGVN=1110
|
||||
* ${test.main.class}
|
||||
* @run main ${test.main.class}
|
||||
*
|
||||
*/
|
||||
|
||||
public class TestModIdealCreatesUselessNode {
|
||||
static int test0(int x) {
|
||||
return x % Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
static long test1(long x) {
|
||||
return x % Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
test0(0);
|
||||
test1(0L);
|
||||
}
|
||||
}
|
||||
@ -274,6 +274,7 @@ public final class Operations {
|
||||
ops.add(Expression.make(BOOLEANS, "Boolean.logicalXor(", BOOLEANS, ", ", BOOLEANS, ")"));
|
||||
|
||||
// TODO: Math and other classes.
|
||||
// Note: Math.copySign is non-deterministic because of NaN having encoding with sign bit set and unset.
|
||||
|
||||
// Make sure the list is not modifiable.
|
||||
return List.copyOf(ops);
|
||||
@ -294,7 +295,8 @@ public final class Operations {
|
||||
ops.add(Expression.make(INTS, "Float16.compare(", FLOAT16, ",", FLOAT16, ")"));
|
||||
addComparisonOperations(ops, "Float16.compare", FLOAT16);
|
||||
ops.add(Expression.make(INTS, "(", FLOAT16, ").compareTo(", FLOAT16, ")"));
|
||||
ops.add(Expression.make(FLOAT16, "Float16.copySign(", FLOAT16, ",", FLOAT16, ")"));
|
||||
// Note: There are NaN encodings with bit set or unset.
|
||||
ops.add(Expression.make(FLOAT16, "Float16.copySign(", FLOAT16, ",", FLOAT16, ")", WITH_NONDETERMINISTIC_RESULT));
|
||||
ops.add(Expression.make(FLOAT16, "Float16.divide(", FLOAT16, ",", FLOAT16, ")"));
|
||||
ops.add(Expression.make(BOOLEANS, "", FLOAT16, ".equals(", FLOAT16, ")"));
|
||||
// Note: there are multiple NaN values with different bit representations.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -45,8 +45,9 @@ abstract class TestTask implements Runnable {
|
||||
}
|
||||
|
||||
public void ensureReadyAndWaiting(Thread vt, Thread.State expState, ReentrantLock rlock) {
|
||||
sleep(50); // reliability: wait for a potential class loading to complete
|
||||
// wait while the thread is not ready or thread state is unexpected
|
||||
while (!threadReady || (vt.getState() != expState) || !rlock.hasQueuedThreads()) {
|
||||
while (!threadReady || (vt.getState() != expState) || !rlock.hasQueuedThread(vt)) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
@ -125,11 +126,12 @@ public class ThreadListStackTracesTest {
|
||||
int jvmtiExpState = (expState == Thread.State.WAITING) ?
|
||||
JVMTI_THREAD_STATE_WAITING :
|
||||
JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER;
|
||||
Thread.State state = vt.getState();
|
||||
|
||||
System.out.printf("State: expected: %s single: %x multi: %x\n",
|
||||
vt.getState(), singleState, multiState);
|
||||
System.out.printf("State: expected: %s, vt.getState(): %s, jvmtiExpState: %x single: %x multi: %x\n",
|
||||
expState, state, jvmtiExpState, singleState, multiState);
|
||||
|
||||
if (vt.getState() != expState) {
|
||||
if (state != expState) {
|
||||
failed("Java thread state is wrong");
|
||||
}
|
||||
if ((singleState & jvmtiExpState) == 0) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user