diff --git a/src/java.base/share/data/tzdata/VERSION b/src/java.base/share/data/tzdata/VERSION index 7602b7afe4c3..0b4358e49c9c 100644 --- a/src/java.base/share/data/tzdata/VERSION +++ b/src/java.base/share/data/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2026b +tzdata2026c diff --git a/src/java.base/share/data/tzdata/africa b/src/java.base/share/data/tzdata/africa index cf1247ee3ab6..a00c3b925e39 100644 --- a/src/java.base/share/data/tzdata/africa +++ b/src/java.base/share/data/tzdata/africa @@ -620,6 +620,24 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # Morocco # See Africa/Ceuta for Spanish Morocco. +# From Paul Eggert (2026-06-26): +# In “Morocco’s GMT+1, a century of shifting time and a debate far from over” +# https://en.yabiladi.com/articles/details/191310/morocco-s-gmt1-century-shifting-time +# (2026-03-30), Yabiladi’s Latifa Babas reports the following: +# * A 1913-10-26 royal dahir established GMT as legal time in Morocco. +# * A 1918-05-10 dahir instituted DST on 1918-05-16 at 00:00. +# * A September 1939 dahir restarted DST on 1939-09-12. +# * A February 1940 dahir restarted DST as early as 1940-02-25. +# * Standard time resumed in September 1946. +# * A June 1950 decree restarted DST, which ran until as late as October. +# * Royal decree 455-67 (1967-06-02) restarted DST on June 3 at noon. +# * After 1967 Morocco used DST “during several summers, particularly +# throughout the 1970s and 1980s.” +# Babas consulted official records that disagree with and are surely +# more correct than our pre-2008 timestamp data, which came from the +# unreliable Shanks & Pottenger. Unfortunately, Babas did not provide +# enough detail to correct our data. + # From Alex Krivenyshev (2008-05-09): # Here is an article that Morocco plan to introduce Daylight Saving Time between # 1 June, 2008 and 27 September, 2008. @@ -873,33 +891,16 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # The return to legal GMT time will take place this Sunday, March 19 at 3 a.m. # ... the return to GMT+1 will be made on Sunday April 23, 2023 at 2 a.m. # https://www.mmsp.gov.ma/fr/actualites/passage-à-l%E2%80%99heure-gmt-à-partir-du-dimanche-19-mars-2023 -# -# From Paul Eggert (2023-03-14): -# For now, guess that in the future Morocco will fall back at 03:00 -# the last Sunday before Ramadan, and spring forward at 02:00 the -# first Sunday after one day after Ramadan. To implement this, -# transition dates and times for 2019 through 2087 were determined by -# running the following program under GNU Emacs 28.2. (This algorithm -# also produces the correct transition dates for 2016 through 2018, -# though the times differ due to Morocco's time zone change in 2018.) -# (let ((islamic-year 1440)) -# (require 'cal-islam) -# (while (< islamic-year 1511) -# (let ((a (calendar-islamic-to-absolute (list 9 1 islamic-year))) -# (b (+ 1 (calendar-islamic-to-absolute (list 10 1 islamic-year)))) -# (sunday 0)) -# (while (/= sunday (mod (setq a (1- a)) 7))) -# (while (/= sunday (mod b 7)) -# (setq b (1+ b))) -# (setq a (calendar-gregorian-from-absolute a)) -# (setq b (calendar-gregorian-from-absolute b)) -# (insert -# (format -# (concat "Rule\tMorocco\t%d\tonly\t-\t%s\t%2d\t 3:00\t-1:00\t-\n" -# "Rule\tMorocco\t%d\tonly\t-\t%s\t%2d\t 2:00\t0\t-\n") -# (car (cdr (cdr a))) (calendar-month-name (car a) t) (car (cdr a)) -# (car (cdr (cdr b))) (calendar-month-name (car b) t) (car (cdr b))))) -# (setq islamic-year (+ 1 islamic-year)))) + +# From Paul Eggert (2026-06-25): +# https://www.moroccoworldnews.com/2026/06/325034/confirmed-morocco-to-restore-gmt-on-september-20-ending-eight-year-gmt1-saga/ +# Today the Moroccan government adopted Decree No. 2.26.530, which abrogates +# the 2018 decree that put it at +01 with daylight saving during Ramadan. +# The plan is to go back to +00 without DST on 2026-09-20 at 02:00. +# From Anass Taghjichte (2026-07-03): +# https://www.sgg.gov.ma/BO/AR/3111/2026/BO_7521_Ar.pdf +# From Afaf EL MAAYATI (2026-07-06): +# https://www.mapexpress.ma/actualite/activite-gouvernementale/conseil-gouvernement-approuve-projet-decret-relatif-au-retour-lheure-legale/ # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Morocco 1939 only - Sep 12 0:00 1:00 - @@ -959,143 +960,14 @@ Rule Morocco 2025 only - Feb 23 3:00 -1:00 - Rule Morocco 2025 only - Apr 6 2:00 0 - Rule Morocco 2026 only - Feb 15 3:00 -1:00 - Rule Morocco 2026 only - Mar 22 2:00 0 - -Rule Morocco 2027 only - Feb 7 3:00 -1:00 - -Rule Morocco 2027 only - Mar 14 2:00 0 - -Rule Morocco 2028 only - Jan 23 3:00 -1:00 - -Rule Morocco 2028 only - Mar 5 2:00 0 - -Rule Morocco 2029 only - Jan 14 3:00 -1:00 - -Rule Morocco 2029 only - Feb 18 2:00 0 - -Rule Morocco 2029 only - Dec 30 3:00 -1:00 - -Rule Morocco 2030 only - Feb 10 2:00 0 - -Rule Morocco 2030 only - Dec 22 3:00 -1:00 - -Rule Morocco 2031 only - Jan 26 2:00 0 - -Rule Morocco 2031 only - Dec 14 3:00 -1:00 - -Rule Morocco 2032 only - Jan 18 2:00 0 - -Rule Morocco 2032 only - Nov 28 3:00 -1:00 - -Rule Morocco 2033 only - Jan 9 2:00 0 - -Rule Morocco 2033 only - Nov 20 3:00 -1:00 - -Rule Morocco 2033 only - Dec 25 2:00 0 - -Rule Morocco 2034 only - Nov 5 3:00 -1:00 - -Rule Morocco 2034 only - Dec 17 2:00 0 - -Rule Morocco 2035 only - Oct 28 3:00 -1:00 - -Rule Morocco 2035 only - Dec 9 2:00 0 - -Rule Morocco 2036 only - Oct 19 3:00 -1:00 - -Rule Morocco 2036 only - Nov 23 2:00 0 - -Rule Morocco 2037 only - Oct 4 3:00 -1:00 - -Rule Morocco 2037 only - Nov 15 2:00 0 - -Rule Morocco 2038 only - Sep 26 3:00 -1:00 - -Rule Morocco 2038 only - Oct 31 2:00 0 - -Rule Morocco 2039 only - Sep 18 3:00 -1:00 - -Rule Morocco 2039 only - Oct 23 2:00 0 - -Rule Morocco 2040 only - Sep 2 3:00 -1:00 - -Rule Morocco 2040 only - Oct 14 2:00 0 - -Rule Morocco 2041 only - Aug 25 3:00 -1:00 - -Rule Morocco 2041 only - Sep 29 2:00 0 - -Rule Morocco 2042 only - Aug 10 3:00 -1:00 - -Rule Morocco 2042 only - Sep 21 2:00 0 - -Rule Morocco 2043 only - Aug 2 3:00 -1:00 - -Rule Morocco 2043 only - Sep 13 2:00 0 - -Rule Morocco 2044 only - Jul 24 3:00 -1:00 - -Rule Morocco 2044 only - Aug 28 2:00 0 - -Rule Morocco 2045 only - Jul 9 3:00 -1:00 - -Rule Morocco 2045 only - Aug 20 2:00 0 - -Rule Morocco 2046 only - Jul 1 3:00 -1:00 - -Rule Morocco 2046 only - Aug 5 2:00 0 - -Rule Morocco 2047 only - Jun 23 3:00 -1:00 - -Rule Morocco 2047 only - Jul 28 2:00 0 - -Rule Morocco 2048 only - Jun 7 3:00 -1:00 - -Rule Morocco 2048 only - Jul 19 2:00 0 - -Rule Morocco 2049 only - May 30 3:00 -1:00 - -Rule Morocco 2049 only - Jul 4 2:00 0 - -Rule Morocco 2050 only - May 15 3:00 -1:00 - -Rule Morocco 2050 only - Jun 26 2:00 0 - -Rule Morocco 2051 only - May 7 3:00 -1:00 - -Rule Morocco 2051 only - Jun 18 2:00 0 - -Rule Morocco 2052 only - Apr 28 3:00 -1:00 - -Rule Morocco 2052 only - Jun 2 2:00 0 - -Rule Morocco 2053 only - Apr 13 3:00 -1:00 - -Rule Morocco 2053 only - May 25 2:00 0 - -Rule Morocco 2054 only - Apr 5 3:00 -1:00 - -Rule Morocco 2054 only - May 10 2:00 0 - -Rule Morocco 2055 only - Mar 28 3:00 -1:00 - -Rule Morocco 2055 only - May 2 2:00 0 - -Rule Morocco 2056 only - Mar 12 3:00 -1:00 - -Rule Morocco 2056 only - Apr 23 2:00 0 - -Rule Morocco 2057 only - Mar 4 3:00 -1:00 - -Rule Morocco 2057 only - Apr 8 2:00 0 - -Rule Morocco 2058 only - Feb 17 3:00 -1:00 - -Rule Morocco 2058 only - Mar 31 2:00 0 - -Rule Morocco 2059 only - Feb 9 3:00 -1:00 - -Rule Morocco 2059 only - Mar 23 2:00 0 - -Rule Morocco 2060 only - Feb 1 3:00 -1:00 - -Rule Morocco 2060 only - Mar 7 2:00 0 - -Rule Morocco 2061 only - Jan 16 3:00 -1:00 - -Rule Morocco 2061 only - Feb 27 2:00 0 - -Rule Morocco 2062 only - Jan 8 3:00 -1:00 - -Rule Morocco 2062 only - Feb 12 2:00 0 - -Rule Morocco 2062 only - Dec 31 3:00 -1:00 - -Rule Morocco 2063 only - Feb 4 2:00 0 - -Rule Morocco 2063 only - Dec 16 3:00 -1:00 - -Rule Morocco 2064 only - Jan 27 2:00 0 - -Rule Morocco 2064 only - Dec 7 3:00 -1:00 - -Rule Morocco 2065 only - Jan 11 2:00 0 - -Rule Morocco 2065 only - Nov 22 3:00 -1:00 - -Rule Morocco 2066 only - Jan 3 2:00 0 - -Rule Morocco 2066 only - Nov 14 3:00 -1:00 - -Rule Morocco 2066 only - Dec 26 2:00 0 - -Rule Morocco 2067 only - Nov 6 3:00 -1:00 - -Rule Morocco 2067 only - Dec 11 2:00 0 - -Rule Morocco 2068 only - Oct 21 3:00 -1:00 - -Rule Morocco 2068 only - Dec 2 2:00 0 - -Rule Morocco 2069 only - Oct 13 3:00 -1:00 - -Rule Morocco 2069 only - Nov 17 2:00 0 - -Rule Morocco 2070 only - Oct 5 3:00 -1:00 - -Rule Morocco 2070 only - Nov 9 2:00 0 - -Rule Morocco 2071 only - Sep 20 3:00 -1:00 - -Rule Morocco 2071 only - Nov 1 2:00 0 - -Rule Morocco 2072 only - Sep 11 3:00 -1:00 - -Rule Morocco 2072 only - Oct 16 2:00 0 - -Rule Morocco 2073 only - Aug 27 3:00 -1:00 - -Rule Morocco 2073 only - Oct 8 2:00 0 - -Rule Morocco 2074 only - Aug 19 3:00 -1:00 - -Rule Morocco 2074 only - Sep 30 2:00 0 - -Rule Morocco 2075 only - Aug 11 3:00 -1:00 - -Rule Morocco 2075 only - Sep 15 2:00 0 - -Rule Morocco 2076 only - Jul 26 3:00 -1:00 - -Rule Morocco 2076 only - Sep 6 2:00 0 - -Rule Morocco 2077 only - Jul 18 3:00 -1:00 - -Rule Morocco 2077 only - Aug 22 2:00 0 - -Rule Morocco 2078 only - Jul 10 3:00 -1:00 - -Rule Morocco 2078 only - Aug 14 2:00 0 - -Rule Morocco 2079 only - Jun 25 3:00 -1:00 - -Rule Morocco 2079 only - Aug 6 2:00 0 - -Rule Morocco 2080 only - Jun 16 3:00 -1:00 - -Rule Morocco 2080 only - Jul 21 2:00 0 - -Rule Morocco 2081 only - Jun 1 3:00 -1:00 - -Rule Morocco 2081 only - Jul 13 2:00 0 - -Rule Morocco 2082 only - May 24 3:00 -1:00 - -Rule Morocco 2082 only - Jun 28 2:00 0 - -Rule Morocco 2083 only - May 16 3:00 -1:00 - -Rule Morocco 2083 only - Jun 20 2:00 0 - -Rule Morocco 2084 only - Apr 30 3:00 -1:00 - -Rule Morocco 2084 only - Jun 11 2:00 0 - -Rule Morocco 2085 only - Apr 22 3:00 -1:00 - -Rule Morocco 2085 only - May 27 2:00 0 - -Rule Morocco 2086 only - Apr 14 3:00 -1:00 - -Rule Morocco 2086 only - May 19 2:00 0 - -Rule Morocco 2087 only - Mar 30 3:00 -1:00 - -Rule Morocco 2087 only - May 11 2:00 0 - -# For dates after the somewhat-arbitrary cutoff of 2087, assume that -# Morocco will no longer observe DST. At some point this table will -# need to be extended, though quite possibly Morocco will change the -# rules first. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Casablanca -0:30:20 - LMT 1913 Oct 26 0:00 Morocco %z 1984 Mar 16 1:00 - %z 1986 0:00 Morocco %z 2018 Oct 28 3:00 - 1:00 Morocco %z + 1:00 Morocco %z 2026 Sep 20 2:00 + 0:00 - %z # Western Sahara # @@ -1111,7 +983,8 @@ Zone Africa/Casablanca -0:30:20 - LMT 1913 Oct 26 Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan # El Aaiún -1:00 - %z 1976 Apr 14 0:00 Morocco %z 2018 Oct 28 3:00 - 1:00 Morocco %z + 1:00 Morocco %z 2026 Sep 20 2:00 + 0:00 - %z # Botswana # Burundi diff --git a/src/java.base/share/data/tzdata/australasia b/src/java.base/share/data/tzdata/australasia index 638789f42da8..04a668ecd5b2 100644 --- a/src/java.base/share/data/tzdata/australasia +++ b/src/java.base/share/data/tzdata/australasia @@ -1744,7 +1744,7 @@ Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila # to Japanese rule was right before 1970, ... per the current tz database # rule, the information doesn't warrant creation of a new timezone for Bonin # Islands itself and is thus as an anecdotal note for interest purpose only. -# ... [The abovementioned link] described some special timekeeping phenomenon +# ... [The link mentioned above] described some special timekeeping phenomenon # regarding Marcus island, another remote island currently owned by Japanese # in the same administrative unit as Bonin Islands. Many reports claim that # the American coastal guard on the American quarter of the island use its own diff --git a/src/java.base/share/data/tzdata/europe b/src/java.base/share/data/tzdata/europe index a66d40834cd2..ca09f4fa3edd 100644 --- a/src/java.base/share/data/tzdata/europe +++ b/src/java.base/share/data/tzdata/europe @@ -253,7 +253,7 @@ # https://www.polyomino.org.uk/british-time/bbc-19410418.png # https://www.polyomino.org.uk/british-time/ho-19410421.png -# From Sir Alexander Maxwell in the above-mentioned letter (1941-04-21): +# From Sir Alexander Maxwell (1941-04-21) in the letter mentioned above: # [N]o official designation has as far as I know been adopted for the time # which is to be introduced in May.... # I cannot think of anything better than "Double British Summer Time" @@ -2038,7 +2038,7 @@ Zone Europe/Malta 0:58:04 - LMT 1893 Nov 2 # Valletta # From Roman Tudos (2015-07-02): # http://lex.justice.md/index.php?action=view&view=doc&lang=1&id=355077 # From Paul Eggert (2015-07-01): -# The abovementioned official link to IGO1445-868/2014 states that +# The above-mentioned official link to IGO1445-868/2014 states that # 2014-10-26's fallback transition occurred at 03:00 local time. Also, # https://www.trm.md/en/social/la-30-martie-vom-trece-la-ora-de-vara # says the 2014-03-30 spring-forward transition was at 02:00 local time. @@ -2403,10 +2403,17 @@ Zone Europe/Lisbon -0:36:45 - LMT 1884 # https://oal.ul.pt/hora-legal/legislacao/ # working backward through references of revocation and abrogation to # Decreto-Lei 47233 of 1966-10-01, the last time DST was abolished across the -# mainland and its adjacent islands. Because of that reference, it is -# therefore assumed that DST rules in the islands prior to 1966 were like that -# of the mainland, though most legislation of the time didn't explicitly -# specify DST practices for the islands. +# mainland and its adjacent islands. +# +# From Tim Parenti (2026-05-26): +# Observance of DST on the Azores and Madeira was explicitly covered by +# mainland legislation in: +# - Portaria 11767 of 1947-03-28 for 1947, +# - Portaria 12286 of 1948-02-19 for 1948, and +# - Decreto-Lei 37048 of 1948-09-07 through its revocation in 1966. +# (See mainland commentary, above.) However, most legislation prior to 1947 +# didn't explicitly call out these "adjacent islands", so we assume that DST +# rules on the islands prior to 1947 were also like that of the mainland. Zone Atlantic/Azores -1:42:40 - LMT 1884 # Ponta Delgada -1:54:32 - HMT 1912 Jan 1 2:00u # Horta MT # Vanguard section, for zic and other parsers that support %z. @@ -2639,7 +2646,7 @@ Zone Europe/Bucharest 1:44:24 - LMT 1891 Oct # http://astro.uni-altai.ru/~orion/blog/2011/11/novyie-granitsyi-chasovyih-poyasov-v-sssr/ # # From Paul Eggert (2018-07-16): -# Perhaps someone could translate the above-mentioned link and use it +# Perhaps someone could translate the link mentioned above, and use it # to correct our data for the ex-Soviet Union. It cites the following: # «Поясное время и новые границы часовых поясов» / сост. П.Н. Долгов, # отв. ред. Г.Д. Бурдун - М: Комитет стандартов, мер и измерительных diff --git a/src/java.base/share/data/tzdata/leapseconds b/src/java.base/share/data/tzdata/leapseconds index d431a7d36079..179e0e9bcc7d 100644 --- a/src/java.base/share/data/tzdata/leapseconds +++ b/src/java.base/share/data/tzdata/leapseconds @@ -93,7 +93,7 @@ Leap 2016 Dec 31 23:59:60 + S # Any additional leap seconds will come after this. # This Expires line is commented out for now, # so that pre-2020a zic implementations do not reject this file. -#Expires 2026 Dec 28 00:00:00 +#Expires 2027 Jun 28 00:00:00 # Here are POSIX timestamps for the data in this file. # "#updated" gives the last time the leap seconds data changed @@ -102,8 +102,8 @@ Leap 2016 Dec 31 23:59:60 + S # "#expires" gives the first time this file might be wrong; # if this file was derived from the IERS leap-seconds.list, # this is typically a bit less than one year after "updated". -#updated 1767698058 (2026-01-06 11:14:18 UTC) -#expires 1798416000 (2026-12-28 00:00:00 UTC) +#updated 1783323897 (2026-07-06 07:44:57 UTC) +#expires 1814140800 (2027-06-28 00:00:00 UTC) # Updated through IERS Bulletin C (https://hpiers.obspm.fr/iers/bul/bulc/bulletinc.dat) -# File expires on 28 December 2026 +# File expires on 28 June 2027 diff --git a/src/java.base/share/data/tzdata/northamerica b/src/java.base/share/data/tzdata/northamerica index 88e4e853f58f..f072cee702a2 100644 --- a/src/java.base/share/data/tzdata/northamerica +++ b/src/java.base/share/data/tzdata/northamerica @@ -1765,6 +1765,22 @@ Zone America/Toronto -5:17:32 - LMT 1895 # Manitoba +# From Paul Eggert (2026-05-08): +# For 1916 timestamps America/Winnipeg covers only a small region. See: +# Cassidy C. Winnipeg’s 110-year history with daylight time. +# Winnipeg Free Press. 2026-05-06. +# https://www.winnipegfreepress.com/our-communities/correspondents/2026/05/06/winnipegs-110-year-history-with-daylight-time +# Of the 1916 experiment, Cassidy writes: “As rural areas and nearby +# urban centres such as Selkirk and Brandon did not adopt DST, the +# City of Winnipeg essentially had its own time zone.” Cassidy also +# writes that province-wide DST came into effect on 1963-05-12. +# +# Shanks & Pottenger write that Winnipeg did not observe DST in 1964 and 1965. +# Although dubious in the light of Cassidy’s article, we lack a better source. +# Perhaps S&P’s data are for the train stations, not for the city? +# Also, S&P say Manitoba switched at 02:00 (not 02:00s) starting in 1966. +# Since 02:00s is clearly correct for 1967 on, assume 02:00s in 1966 too. + # From Rob Douglas (2006-04-06): # the old Manitoba Time Act - as amended by Bill 2, assented to # March 27, 1987 ... said ... @@ -1779,11 +1795,6 @@ Zone America/Toronto -5:17:32 - LMT 1895 # the 1987 version would apply - the changeover was at 2:00 Central # Standard Time (i.e. not until 3:00 Central Daylight Time). -# From Paul Eggert (2006-04-10): -# Shanks & Pottenger say Manitoba switched at 02:00 (not 02:00s) -# starting 1966. Since 02:00s is clearly correct for 1967 on, assume -# it was also 02:00s in 1966. - # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Winn 1916 only - Apr 23 0:00 1:00 D Rule Winn 1916 only - Sep 17 0:00 0 S @@ -1875,6 +1886,22 @@ Zone America/Winnipeg -6:28:36 - LMT 1887 Jul 16 # long and rather painful to read. # http://www.qp.gov.sk.ca/documents/English/Statutes/Statutes/T14.pdf +# From Heitor David Pinto (2026-05-14): +# In Saskatchewan, a bill was passed to replace the Time Act. It sets UTC-6 all +# year in the whole province, including Lloydminster, but allows the government +# to issue regulations specifying a different time in localities that request +# so: +# https://docs.legassembly.sk.ca/legdocs/Bills/30L2S/Bill30-58.pdf +# The bill ... received royal assent today. +# From Tim Parenti (2026-05-14): +# In light of Alberta joining Saskatchewan on year-round -06, this simplifies +# the prior Act's framework for the many local exceptions to year-round -06 in +# border areas, by extending province-wide the notion of "time option areas" +# which can be prescribed by regulation "if it is in the provincial interest" +# for those areas to observe either "UTC-5, UTC-6 or UTC-7 for all or part of +# the year" on at least 30 days' notice. +# The new Act comes into force by order of the Lieutenant Governor in Council. + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Regina 1918 only - Apr 14 2:00 1:00 D Rule Regina 1918 only - Oct 27 2:00 0 S @@ -1925,6 +1952,45 @@ Zone America/Swift_Current -7:11:20 - LMT 1905 Sep # Boyer JP. Forcing Choice: The Risky Reward of Referendums. Dundum. 2017. # ISBN 978-1459739123. +# From Roozbeh Pournader (2026-04-20): +# https://calgaryherald.com/opinion/columnists/bell-alberta-daylight-time-year-round-premier-danielle-smith +# +# From Tim Parenti (2026-04-23): +# Section 3 of Bill 31, the Red Tape Reduction Statutes Amendment Act, 2026 +# https://docs.assembly.ab.ca/LADDAR_files/docs/bills/bill/legislature_31/session_2/20251023_bill-031.pdf +# would repeal the Daylight Saving Time Act in the Revised Statutes of Alberta +# 2000 Chapter D-5: +# https://kings-printer.alberta.ca/documents/Acts/D05.pdf +# ...and substitutes a new chapter with language that closely parallels the +# original. The new title is the Official Time Act and will be numbered +# Chapter O-5.7. The Act establishes a standard time of UTC−6 without +# replacing the language previously used to effectuate DST. +# +# From Tim Parenti (2026-06-19): +# After receiving Royal Assent on 2026-05-14, Order in Council 204/2026 was +# issued on 2026-06-18 proclaiming the relevant section of the bill in force on +# the same date. Order in Council 206/2026, issued the same day, uses the +# regulatory authority within the Act to prescribe the official term "Alberta +# Time"; we use the traditional abbreviation CST for consistency. +# https://kings-printer.alberta.ca/Documents/Orders/Orders_in_Council/2026/2026_204.pdf +# https://kings-printer.alberta.ca/Documents/Orders/Orders_in_Council/2026/2026_206.pdf +# +# Since wall clock times do not diverge from past practice until 2026-11-01, +# use that transition date for now to work around potential CLDR limitations in +# the meantime; see British Columbia, below. +# +# From Paul Eggert (2026-07-02): +# The temporary hack for Alberta is needed for CLDR 48.2 (2026-03-17) +# and earlier, not the CLDR 48.1-and-earlier which drives BC’s temporary hack. +# Only a few platforms track minor CLDR releases, though, so the two +# temporary hacks have roughly the same effect in practice. +# +# The term “Alberta Time” is legally prescribed from yesterday until +# 2031-06-30, when the regulation in OiC 206/2026 expires to ensure that the +# term is reviewed by then for relevancy and need. This plan for possible +# obsolescence affects neither timekeeping nor TZDB’s data, which do +# not contain the string “Alberta Time”. + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Edm 1918 1919 - Apr Sun>=8 2:00 1:00 D Rule Edm 1918 only - Oct 27 2:00 0 S @@ -1942,7 +2008,11 @@ Rule Edm 1972 2006 - Oct lastSun 2:00 0 S # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Edmonton -7:33:52 - LMT 1906 Sep -7:00 Edm M%sT 1987 - -7:00 Canada M%sT + -7:00 Canada M%sT 2026 Jun 18 + # Temporary hack; see above. + -7:00 1:00 MDT 2026 Nov 1 2:00 + # End of temporary hack. + -6:00 - CST # British Columbia @@ -1985,7 +2055,7 @@ Zone America/Edmonton -7:33:52 - LMT 1906 Sep # on March 8 will be the last time change, ending twice-yearly clock changes.” # https://news.gov.bc.ca/releases/2026AG0013-000209 # -# From Paul Eggert (2026-03-07): +# From Paul Eggert (2026-07-02): # The law says that 21 hours after the usual 2026-03-08 02:00 switch from # PST to PDT, the next day inaugurates the new standard time Pacific Time, # i.e., just one clock change but two name changes separated by 21 hours. @@ -1994,13 +2064,14 @@ Zone America/Edmonton -7:33:52 - LMT 1906 Sep # I asked the BC government for advice, with no response. For now, do this: # 1. As a temporary hack, pretend that the BC law takes effect # not on 2026-03-09 at 00:00, but on 2026-11-01 at 02:00. -# This pretense works around a limitation in CLDR v48.2 (2026-03-17), +# This pretense works around a limitation in CLDR 48.1 (2026-01-08), # which would otherwise say the interval uses “Pacific Standard Time”. # (Below, this temporary hack is marked “Temporary hack; see above.”) # Strictly speaking this hack is incorrect since the interval uses # standard time, but it does have the right UT offset and it # works around the CLDR limitation. We should be able to remove -# the temporary hack after CLDR is fixed. +# the temporary hack by November when there would be little point +# to keeping it anyway. # 2. After the BC law takes effect, model the time as MST sans DST. # We can change this later if another conforming non-numeric abbreviation # for Pacific Time becomes more popular. Possibilities include: @@ -2011,10 +2082,7 @@ Zone America/Edmonton -7:33:52 - LMT 1906 Sep # PST - straightforward but even more confusing, # and will likely break much software that assumes PST is -08 # -07 - accurate and clear in itself, but makes BC look odd vs neighbors -# CPT, CPST - for Canadian Pacific (Standard) Time, -# by analogy with AEST in Australia -# P-T - conforming approximation to “PT” -# PT+ - like P-T but suggesting one-hour advance over PST +# PacT - straightforward but novel abbreviation for Pacific Time # From Chris Walton (2026-03-15): # The Regional District of East Kootenay is planning to move to year-round @@ -2029,6 +2097,10 @@ Zone America/Edmonton -7:33:52 - LMT 1906 Sep # saying, “Pardon the pun, but this is not a time-sensitive issue.” # For now, merely mention the potential change in these comments. # If it happens it would likely affect clocks starting 2027-03-14 at 02:00. +# From Tim Parenti (2026-05-14): +# RDEK has historically been aligned with neighboring Alberta. With the latter +# now opting to stay on -06 year-round, if RDEK does not follow, it would +# require a new zone for the Cranbrook area. # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Vanc 1918 only - Apr 14 2:00 1:00 D @@ -2045,7 +2117,7 @@ Zone America/Vancouver -8:12:28 - LMT 1884 -8:00 Vanc P%sT 1987 -8:00 Canada P%sT 2026 Mar 9 # Temporary hack; see above. - -8:00 1:00 PDT 2026 Nov 1 02:00 + -8:00 1:00 PDT 2026 Nov 1 2:00 # End of temporary hack. -7:00 - MST Zone America/Dawson_Creek -8:00:56 - LMT 1884 @@ -2373,6 +2445,23 @@ Zone America/Fort_Nelson -8:10:47 - LMT 1884 # about 1970, and uses PST for standard time in Yukon since then. Consistent # with that, use MST for -07, the new standard time in Yukon effective Nov. 1. +# From Tim Parenti (2026-04-21): +# "[Northwest Territories] Premier R.J. Simpson announced Monday that the +# territory will move to end seasonal time changes and will adopt a year-round +# time standard instead. ... Simpson has previously said the territory wouldn't +# end seasonal time changes until Alberta does." +# https://www.cbc.ca/news/canada/north/nwt-ends-daylight-saving-9.7170964 +# +# From Tim Parenti (2026-06-19), per James Bellaire (2026-06-02): +# Much of NWT has, to date, been represented by America/Edmonton, which alias +# America/Yellowknife links to. While Bill 13 (assented to 2021-03-31) would +# enable NWT's proposed change mirroring Alberta's, at time of writing it has +# not yet been formally enacted; if it doesn't move forward as expected, +# America/Yellowknife would need to become its own zone as Alberta has stopped +# changing its clocks. +# If it does go ahead, draft changes to America/Inuvik, which represents the +# remainder of NWT, are commented below. + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule NT_YK 1918 only - Apr 14 2:00 1:00 D Rule NT_YK 1918 only - Oct 27 2:00 0 S @@ -2415,6 +2504,10 @@ Zone America/Inuvik 0 - -00 1953 # Inuvik founded -8:00 NT_YK P%sT 1979 Apr lastSun 2:00 -7:00 NT_YK M%sT 1980 -7:00 Canada M%sT +# Assuming Northwest Territories follows Alberta in abolishing seasonal time +# changes, replace the above line with something like: +# -7:00 Canada M%sT 2026 Nov 1 2:00 +# -6:00 - CST Zone America/Whitehorse -9:00:12 - LMT 1900 Aug 20 -9:00 NT_YK Y%sT 1965 -9:00 Yukon Y%sT 1966 Feb 27 0:00 diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultStripDebugPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultStripDebugPlugin.java index b3644bdde857..283ca11deccb 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultStripDebugPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultStripDebugPlugin.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,12 +28,13 @@ import java.util.Map; +import jdk.tools.jlink.internal.Platform; import jdk.tools.jlink.internal.PluginRepository; import jdk.tools.jlink.internal.ResourcePoolManager; -import jdk.tools.jlink.internal.ResourcePoolManager.ResourcePoolImpl; import jdk.tools.jlink.plugin.Plugin; import jdk.tools.jlink.plugin.ResourcePool; import jdk.tools.jlink.plugin.ResourcePoolBuilder; +import jdk.tools.jlink.plugin.ResourcePoolModule; /** * Combined debug stripping plugin: Java debug attributes and native debug @@ -43,6 +45,7 @@ public final class DefaultStripDebugPlugin extends AbstractPlugin { private static final String STRIP_NATIVE_DEBUG_PLUGIN = "strip-native-debug-symbols"; private static final String EXCLUDE_DEBUGINFO = "exclude-debuginfo-files"; + private static final String EXCLUDE_FILES_PLUGIN = "exclude-files"; private final Plugin javaStripPlugin; private final NativePluginFactory stripNativePluginFactory; @@ -68,26 +71,46 @@ public void enableJavaStripPlugin(boolean enableJavaStripPlugin) { @Override public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { Plugin stripNativePlugin = stripNativePluginFactory.create(); + + String pattern = debugFilePattern(in); + ExcludeFilesPlugin excludeFilesPlugin = new ExcludeFilesPlugin(); + excludeFilesPlugin.configure(Map.of(EXCLUDE_FILES_PLUGIN, pattern)); + + ResourcePool result = in; + if (isJavaStripPluginEnabled) { + result = pipe(result, javaStripPlugin); + } if (stripNativePlugin != null) { - Map stripNativeConfig = Map.of( - STRIP_NATIVE_DEBUG_PLUGIN, EXCLUDE_DEBUGINFO); - stripNativePlugin.configure(stripNativeConfig); - - if (!isJavaStripPluginEnabled) { - return stripNativePlugin.transform(in, out); - } - - ResourcePoolManager outRes = - new ResourcePoolManager(in.byteOrder(), - ((ResourcePoolImpl)in).getStringTable()); - ResourcePool strippedJava = javaStripPlugin.transform(in, - outRes.resourcePoolBuilder()); - return stripNativePlugin.transform(strippedJava, out); - } else if (isJavaStripPluginEnabled) { - return javaStripPlugin.transform(in, out); - } else { - return in; + stripNativePlugin.configure(Map.of(STRIP_NATIVE_DEBUG_PLUGIN, EXCLUDE_DEBUGINFO)); + result = pipe(result, stripNativePlugin); + } + return excludeFilesPlugin.transform(result, out); + } + + // Returns the glob pattern for debug files matching the target platform. + // Mirrors the per-OS exclusion logic in make/CreateJmods.gmk. + private static String debugFilePattern(ResourcePool in) { + Platform platform; + try { + String tp = in.moduleView() + .findModule("java.base") + .map(ResourcePoolModule::targetPlatform) + .orElse(null); + platform = tp != null ? Platform.parsePlatform(tp) : Platform.runtime(); + } catch (IllegalArgumentException e) { + platform = Platform.runtime(); } + return switch (platform.os()) { + case WINDOWS -> "**.pdb,**.map,**.diz"; + case MACOS -> "**.dSYM/**,**.diz"; + default -> "**.debuginfo,**.diz"; // Linux, AIX + }; + } + + private ResourcePool pipe(ResourcePool pool, Plugin plugin) { + ResourcePoolManager mgr = new ResourcePoolManager( + pool.byteOrder(), ((ResourcePoolManager.ResourcePoolImpl)pool).getStringTable()); + return plugin.transform(pool, mgr.resourcePoolBuilder()); } public interface NativePluginFactory { diff --git a/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java b/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java index 553a7ff6d5b6..0cfe44d3a523 100644 --- a/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java +++ b/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java @@ -28,6 +28,7 @@ import java.text.DateFormatSymbols; import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; import java.time.format.DecimalStyle; @@ -41,6 +42,7 @@ import java.util.Date; import java.util.HashSet; import java.util.Locale; +import java.util.Map; import java.util.Random; import java.util.Set; import java.util.TimeZone; @@ -52,7 +54,7 @@ /* * @test * @bug 8081022 8151876 8166875 8177819 8189784 8206980 8277049 8278434 8346948 - * 8174269 8390388 + * 8174269 8390388 8388214 * @key randomness */ @@ -62,6 +64,17 @@ @Test public class TestZoneTextPrinterParser extends AbstractTestPrinterParser { + // Explicit dstOffset attributes from CLDR pre-release v49 metazone data. + private static final Map CLDR_EXPLICIT_DST_OFFSETS = Map.of( + "Africa/Windhoek", ZoneOffset.of("+02:00"), + "America/Edmonton", ZoneOffset.of("-06:00"), + "America/Yellowknife", ZoneOffset.of("-06:00"), + "America/Vancouver", ZoneOffset.of("-07:00"), + "Canada/Mountain", ZoneOffset.of("-06:00"), + "Canada/Pacific", ZoneOffset.of("-07:00"), + "Europe/Dublin", ZoneOffset.of("+01:00"), + "Eire", ZoneOffset.of("+01:00")); + private static final Locale[] SAMPLE_LOCALES = { Locale.US, Locale.UK, Locale.FRANCE, Locale.GERMANY, Locale.ITALY, Locale.forLanguageTag("es"), Locale.forLanguageTag("pt-BR"), Locale.forLanguageTag("ru"), @@ -92,10 +105,11 @@ public void test_printText() { long epochMilli = zdt.toInstant().toEpochMilli(); boolean isDST = tz.inDaylightTime(new Date(epochMilli)); // Some zones now use an explicit daylight offset in CLDR without java.util.TimeZone - // reporting DST for the instant, so prefer the daylight name when the effective - // offset is greater than the raw standard offset. - boolean useDaylightName = isDST - || (tz.getDSTSavings() == 0 && tz.getOffset(epochMilli) > tz.getRawOffset()); + // reporting DST for the instant. + ZoneOffset explicitDstOffset = CLDR_EXPLICIT_DST_OFFSETS.get(zid); + boolean useDaylightName = explicitDstOffset != null + ? zdt.getOffset().equals(explicitDstOffset) + : isDST; for (Locale locale : SAMPLE_LOCALES) { String longDisplayName = tz.getDisplayName(useDaylightName, TimeZone.LONG, locale); String shortDisplayName = tz.getDisplayName(useDaylightName, TimeZone.SHORT, locale); diff --git a/test/jdk/java/time/test/java/time/zone/TestZoneRules.java b/test/jdk/java/time/test/java/time/zone/TestZoneRules.java index 584ed2e56c78..a5efd6ddae3b 100644 --- a/test/jdk/java/time/test/java/time/zone/TestZoneRules.java +++ b/test/jdk/java/time/test/java/time/zone/TestZoneRules.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, 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 @@ -48,7 +48,7 @@ /** * @summary Tests for ZoneRules class. * - * @bug 8212970 8236903 8239836 + * @bug 8212970 8236903 8239836 8388214 */ @Test public class TestZoneRules { @@ -90,15 +90,17 @@ private Object[][] negativeDST () { {WINDHOEK, LocalDate.of(1994, 3, 23), OFF_1, OFF_1, false}, {WINDHOEK, LocalDate.of(2016, 9, 23), OFF_2, OFF_1, true}, - // Africa/Casablanca for the Rule "Morocco" Defines negative DST till 2037 as of 2019a. + // Africa/Casablanca for the Rule "Morocco" defines negative DST until early 2026, + // then returns to standard UTC permanently later that year, starting with 2026c. {CASABLANCA, LocalDate.of(1939, 9, 13), OFF_1, OFF_0, true}, {CASABLANCA, LocalDate.of(1939, 11, 20), OFF_0, OFF_0, false}, {CASABLANCA, LocalDate.of(2018, 6, 18), OFF_1, OFF_0, true}, {CASABLANCA, LocalDate.of(2019, 1, 1), OFF_1, OFF_0, true}, {CASABLANCA, LocalDate.of(2019, 5, 6), OFF_0, OFF_0, false}, - {CASABLANCA, LocalDate.of(2037, 10, 5), OFF_0, OFF_0, false}, - {CASABLANCA, LocalDate.of(2037, 11, 16), OFF_1, OFF_0, true}, - {CASABLANCA, LocalDate.of(2038, 11, 8), OFF_1, OFF_0, true}, + {CASABLANCA, LocalDate.of(2026, 2, 16), OFF_0, OFF_0, false}, + {CASABLANCA, LocalDate.of(2026, 3, 23), OFF_1, OFF_0, true}, + {CASABLANCA, LocalDate.of(2026, 9, 21), OFF_0, OFF_0, false}, + {CASABLANCA, LocalDate.of(2038, 11, 8), OFF_0, OFF_0, false}, }; } diff --git a/test/jdk/java/util/TimeZone/Bug6329116.java b/test/jdk/java/util/TimeZone/Bug6329116.java index 4f6f5f27eac1..8d80dd660470 100644 --- a/test/jdk/java/util/TimeZone/Bug6329116.java +++ b/test/jdk/java/util/TimeZone/Bug6329116.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, 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 @@ -26,6 +26,7 @@ * @bug 6329116 6756569 6757131 6758988 6764308 6796489 6834474 6609737 6507067 * 7039469 7090843 7103108 7103405 7158483 8008577 8059206 8064560 8072042 * 8077685 8151876 8166875 8169191 8170316 8176044 8174269 8347841 8347955 + * 8388214 * @summary Make sure that timezone short display names are identical to Olson's data. * @run junit Bug6329116 */ @@ -41,6 +42,14 @@ public class Bug6329116 { + // As of CLDR v48.2, CLDR provides these short names for zones with explicit DST offsets. + private static final Map CLDR_SHORT_NAMES = Map.of( + "America/Edmonton", "MST", + "Canada/Mountain", "MST", + "America/Yellowknife", "MST", + "America/Vancouver", "PST", + "Canada/Pacific", "PST"); + // Do not test all locales, as some locales have localized // short names in CLDR. Test only for the US locale @@ -250,6 +259,11 @@ static boolean useLocalizedShortDisplayName(TimeZone tz, return tz.getDisplayName().substring(0, 3).equals(got.substring(0, 3)); } + // Some zones use CLDR short names even when the tzdata FORMAT changed. + if (!inDST) { + return CLDR_SHORT_NAMES.getOrDefault(tz.getID(), "").equals(got); + } + return false; } diff --git a/test/jdk/java/util/TimeZone/NegativeDSTTest.java b/test/jdk/java/util/TimeZone/NegativeDSTTest.java index ab9438f23887..47c35a08147c 100644 --- a/test/jdk/java/util/TimeZone/NegativeDSTTest.java +++ b/test/jdk/java/util/TimeZone/NegativeDSTTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,7 @@ /** * @test - * @bug 8212970 8324065 + * @bug 8212970 8324065 8388214 * @summary Test whether the savings are positive in time zones that have * negative savings in the source TZ files. * @run junit NegativeDSTTest @@ -73,18 +73,17 @@ private Object[][] negativeDST () { {WINDHOEK, LocalDate.of(1994, 3, 23), ONE_HOUR, false}, {WINDHOEK, LocalDate.of(2016, 9, 23), 2 * ONE_HOUR, true}, - // Africa/Casablanca for the Rule "Morocco" Defines negative DST till 2037 as of 2019a. + // Africa/Casablanca for the Rule "Morocco" defines negative DST until early 2026, + // then returns to standard UTC permanently later that year, starting with 2026c. {CASABLANCA, LocalDate.of(1939, 9, 13), ONE_HOUR, true}, {CASABLANCA, LocalDate.of(1939, 11, 20), 0, false}, {CASABLANCA, LocalDate.of(2018, 6, 18), ONE_HOUR, true}, {CASABLANCA, LocalDate.of(2019, 1, 1), ONE_HOUR, true}, {CASABLANCA, LocalDate.of(2019, 5, 6), 0, false}, - {CASABLANCA, LocalDate.of(2037, 10, 5), 0, false}, - {CASABLANCA, LocalDate.of(2037, 11, 16), ONE_HOUR, true}, - {CASABLANCA, LocalDate.of(2038, 9, 27), 0, false}, - {CASABLANCA, LocalDate.of(2038, 11, 1), ONE_HOUR, true}, - {CASABLANCA, LocalDate.of(2087, 3, 31), 0, false}, - {CASABLANCA, LocalDate.of(2087, 5, 12), ONE_HOUR, true}, + {CASABLANCA, LocalDate.of(2026, 2, 16), 0, false}, + {CASABLANCA, LocalDate.of(2026, 3, 23), ONE_HOUR, true}, + {CASABLANCA, LocalDate.of(2026, 9, 21), 0, false}, + {CASABLANCA, LocalDate.of(2038, 11, 1), 0, false}, }; } diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION index 7ae8c6f5c484..5f54cd5a1740 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION +++ b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION @@ -1 +1 @@ -tzdata2026b +tzdata2026c diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt b/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt index e7b6ea8bf3cb..36b682097861 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt +++ b/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt @@ -30,7 +30,7 @@ America/Dawson MST America/Dawson_Creek MST America/Denver MST MDT America/Detroit EST EDT -America/Edmonton MST MDT +America/Edmonton CST America/El_Salvador CST CDT America/Fort_Nelson MST America/Glace_Bay AST ADT @@ -86,7 +86,7 @@ America/Tegucigalpa CST CDT America/Thule AST ADT America/Tijuana PST PDT America/Toronto EST EDT -America/Vancouver PST PDT +America/Vancouver MST America/Whitehorse MST America/Winnipeg CST CDT America/Yakutat AKST AKDT diff --git a/test/jdk/tools/jlink/JLinkReproducible2Test.java b/test/jdk/tools/jlink/JLinkReproducible2Test.java index 4723bd0eb8f0..56593147bfc3 100644 --- a/test/jdk/tools/jlink/JLinkReproducible2Test.java +++ b/test/jdk/tools/jlink/JLinkReproducible2Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ import java.nio.file.Paths; import java.util.spi.ToolProvider; +import jdk.test.lib.util.FileUtils; + /* * @test * @summary Make sure that jimages are consistent when created by jlink. @@ -35,6 +37,8 @@ * jdk.management * jdk.unsupported * jdk.charsets + * @library /test/lib + * @build jdk.test.lib.util.FileUtils * @run main JLinkReproducible2Test */ public class JLinkReproducible2Test { @@ -54,6 +58,10 @@ public static void main(String[] args) throws Exception { throw new RuntimeException("jlink producing inconsistent result"); } + // Free disk space before creating the next pair of images. + FileUtils.deleteFileTreeWithRetry(image1); + FileUtils.deleteFileTreeWithRetry(image2); + Path image3 = Paths.get("./image3"); Path image4 = Paths.get("./image4"); diff --git a/test/jdk/tools/jlink/JLinkReproducible3Test.java b/test/jdk/tools/jlink/JLinkReproducible3Test.java index 6d37cff4ee4d..841fe1fcbb40 100644 --- a/test/jdk/tools/jlink/JLinkReproducible3Test.java +++ b/test/jdk/tools/jlink/JLinkReproducible3Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ */ import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.util.FileUtils; import java.io.File; import java.io.IOException; @@ -40,6 +41,7 @@ * jdk.unsupported * jdk.charsets * @library /test/lib + * @build jdk.test.lib.util.FileUtils * @run main JLinkReproducible3Test */ public class JLinkReproducible3Test { @@ -48,33 +50,17 @@ public static void main(String[] args) throws Exception { Path image1 = Paths.get("./image1"); Path image2 = Paths.get("./image2"); - Path copyJdk1Dir = Path.of("./copy-jdk1-tmpdir"); - Files.createDirectory(copyJdk1Dir); - - Path copyJdk2Dir = Path.of("./copy-jdk2-tmpdir"); - Files.createDirectory(copyJdk2Dir); - Path jdkTestDir = Path.of( Optional.of( System.getProperty("test.jdk")) .orElseThrow(() -> new RuntimeException("Couldn't load JDK Test Dir")) ); - copyJDK(jdkTestDir, copyJdk1Dir); - copyJDK(jdkTestDir, copyJdk2Dir); - - Path copiedJlink1 = Optional.of( - Paths.get(copyJdk1Dir.toString(), "bin", "jlink")) - .orElseThrow(() -> new RuntimeException("Unable to load copied jlink") - ); - - Path copiedJlink2 = Optional.of( - Paths.get(copyJdk2Dir.toString(), "bin", "jlink")) - .orElseThrow(() -> new RuntimeException("Unable to load copied jlink") - ); - - runCopiedJlink(copiedJlink1.toString(), "--add-modules", "java.base,jdk.management,jdk.unsupported,jdk.charsets", "--output", image1.toString()); - runCopiedJlink(copiedJlink2.toString(), "--add-modules", "java.base,jdk.management,jdk.unsupported,jdk.charsets", "--output", image2.toString()); + // Link each image from its own copy of the JDK placed at a distinct + // location. Copy, link, then delete the copy before creating the next + // one so that at most one JDK copy exists on disk at a time. + linkFromCopy(jdkTestDir, Path.of("./copy-jdk1-tmpdir"), image1); + linkFromCopy(jdkTestDir, Path.of("./copy-jdk2-tmpdir"), image2); long mismatch = Files.mismatch(image1.resolve("lib").resolve("modules"), image2.resolve("lib").resolve("modules")); if (mismatch != -1L) { @@ -82,6 +68,20 @@ public static void main(String[] args) throws Exception { } } + private static void linkFromCopy(Path jdkTestDir, Path copyJdkDir, Path image) throws Exception { + Files.createDirectory(copyJdkDir); + copyJDK(jdkTestDir, copyJdkDir); + + Path copiedJlink = Paths.get(copyJdkDir.toString(), "bin", "jlink"); + runCopiedJlink(copiedJlink.toString(), "--add-modules", + "java.base,jdk.management,jdk.unsupported,jdk.charsets", + "--output", image.toString()); + + // The copied JDK was only needed to run jlink; free the disk space + // before creating the next copy. + FileUtils.deleteFileTreeWithRetry(copyJdkDir); + } + private static void runCopiedJlink(String... args) throws Exception { var process = new ProcessBuilder(args); var res = ProcessTools.executeProcess(process); diff --git a/test/jdk/tools/jlink/JLinkTest.java b/test/jdk/tools/jlink/JLinkTest.java index 0b7de201ac9d..f819b7b055b5 100644 --- a/test/jdk/tools/jlink/JLinkTest.java +++ b/test/jdk/tools/jlink/JLinkTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, 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 @@ -36,6 +36,7 @@ import jdk.tools.jlink.internal.PluginRepository; import jdk.tools.jlink.plugin.Plugin; +import jdk.test.lib.util.FileUtils; import tests.Helper; import tests.JImageGenerator; @@ -48,13 +49,14 @@ * @bug 8240349 * @author Jean-Francois Denise * @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g) - * @library ../lib + * @library ../lib /test/lib * @modules java.base/jdk.internal.jimage * jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.plugin * jdk.jlink/jdk.tools.jimage * jdk.compiler * @build tests.* + * @build jdk.test.lib.util.FileUtils * @run main/othervm -Xmx1g JLinkTest */ public class JLinkTest { @@ -113,10 +115,12 @@ public static void main(String[] args) throws Exception { // No --module-path specified. $JAVA_HOME/jmods should be assumed. // The following should succeed as it uses only system modules. String imageDir = "bug818977-no-modulepath"; + Path image = helper.createNewImageDir(imageDir); JImageGenerator.getJLinkTask() - .output(helper.createNewImageDir(imageDir)) + .output(image) .addMods("jdk.jshell") .call().assertSuccess(); + FileUtils.deleteFileTreeWithRetry(image); } { @@ -124,11 +128,13 @@ public static void main(String[] args) throws Exception { // $JAVA_HOME/jmods should be added automatically. // The following should succeed as it uses only system modules. String imageDir = "bug8189777-invalid-modulepath"; + Path image = helper.createNewImageDir(imageDir); JImageGenerator.getJLinkTask() .modulePath("does_not_exist_path") - .output(helper.createNewImageDir(imageDir)) + .output(image) .addMods("jdk.jshell") .call().assertSuccess(); + FileUtils.deleteFileTreeWithRetry(image); } { @@ -142,11 +148,13 @@ public static void main(String[] args) throws Exception { { String moduleName = "bug8134651"; + Path image1 = helper.createNewImageDir(moduleName); JImageGenerator.getJLinkTask() .modulePath(helper.defaultModulePath()) - .output(helper.createNewImageDir(moduleName)) + .output(image1) .addMods("leaf1") .call().assertSuccess(); + FileUtils.deleteFileTreeWithRetry(image1); JImageGenerator.getJLinkTask() .modulePath(helper.defaultModulePath()) .addMods("leaf1") @@ -158,11 +166,13 @@ public static void main(String[] args) throws Exception { .addMods("leaf1") .call().assertFailure("Error: no value given for --module-path"); // do not include standard module path - should be added automatically + Path image2 = helper.createNewImageDir(moduleName); JImageGenerator.getJLinkTask() .modulePath(helper.defaultModulePath(false)) - .output(helper.createNewImageDir(moduleName)) + .output(image2) .addMods("leaf1") .call().assertSuccess(); + FileUtils.deleteFileTreeWithRetry(image2); // no --module-path. default sys mod path is assumed - but that won't contain 'leaf1' module JImageGenerator.getJLinkTask() .output(helper.createNewImageDir(moduleName)) @@ -173,18 +183,22 @@ public static void main(String[] args) throws Exception { { String moduleName = "m"; // 8163382 Path jmod = helper.generateDefaultJModule(moduleName).assertSuccess(); + Path imageM = helper.createNewImageDir(moduleName); JImageGenerator.getJLinkTask() .modulePath(helper.defaultModulePath()) - .output(helper.createNewImageDir(moduleName)) + .output(imageM) .addMods("m") .call().assertSuccess(); + FileUtils.deleteFileTreeWithRetry(imageM); moduleName = "mod"; jmod = helper.generateDefaultJModule(moduleName).assertSuccess(); + Path imageMod = helper.createNewImageDir(moduleName); JImageGenerator.getJLinkTask() .modulePath(helper.defaultModulePath()) - .output(helper.createNewImageDir(moduleName)) + .output(imageMod) .addMods("m") .call().assertSuccess(); + FileUtils.deleteFileTreeWithRetry(imageMod); } { @@ -199,13 +213,15 @@ public static void main(String[] args) throws Exception { // second --module-path does not have that module .call().assertFailure("Error: Module m_8165735 not found"); + Path imageRepeatedPath = helper.createNewImageDir(moduleName); JImageGenerator.getJLinkTask() .modulePath(".") // first --module-path overridden later .repeatedModulePath(helper.defaultModulePath()) - .output(helper.createNewImageDir(moduleName)) + .output(imageRepeatedPath) .addMods(moduleName) // second --module-path has that module .call().assertSuccess(); + FileUtils.deleteFileTreeWithRetry(imageRepeatedPath); JImageGenerator.getJLinkTask() .modulePath(helper.defaultModulePath()) @@ -215,13 +231,15 @@ public static void main(String[] args) throws Exception { .addMods(moduleName) .call().assertFailure("Error: Module m_8165735dependency not found, required by m_8165735"); + Path imageRepeatedLimit = helper.createNewImageDir(moduleName); JImageGenerator.getJLinkTask() .modulePath(helper.defaultModulePath()) - .output(helper.createNewImageDir(moduleName)) + .output(imageRepeatedLimit) .limitMods("java.base") .repeatedLimitMods(moduleName) // second --limit-modules overrides first .addMods(moduleName) .call().assertSuccess(); + FileUtils.deleteFileTreeWithRetry(imageRepeatedLimit); } { @@ -272,6 +290,7 @@ public static void main(String[] args) throws Exception { String[] files = {Helper.getDebugSymbolsExtension()}; Path imageDir = helper.generateDefaultImage(userOptions, moduleName).assertSuccess(); helper.checkImage(imageDir, moduleName, res, files); + FileUtils.deleteFileTreeWithRetry(imageDir); } // filter out + Skip debug + compress with filter + sort resources @@ -285,6 +304,7 @@ public static void main(String[] args) throws Exception { String[] res = {".jcov", "/META-INF/"}; Path imageDir = helper.generateDefaultImage(userOptions2, moduleName).assertSuccess(); helper.checkImage(imageDir, moduleName, res, null); + FileUtils.deleteFileTreeWithRetry(imageDir); } // module-info.class should not be excluded @@ -409,5 +429,6 @@ private static void testCompress(Helper helper, String moduleName, String... use helper.generateDefaultJModule(moduleName, "composite2"); Path imageDir = helper.generateDefaultImage(userOptions, moduleName).assertSuccess(); helper.checkImage(imageDir, moduleName, null, null); + FileUtils.deleteFileTreeWithRetry(imageDir); } } diff --git a/test/jdk/tools/jlink/plugins/DefaultStripDebugPluginTest.java b/test/jdk/tools/jlink/plugins/DefaultStripDebugPluginTest.java index 379b89adc9ff..c0a08093a4a7 100644 --- a/test/jdk/tools/jlink/plugins/DefaultStripDebugPluginTest.java +++ b/test/jdk/tools/jlink/plugins/DefaultStripDebugPluginTest.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,6 +24,7 @@ import java.util.Map; +import jdk.tools.jlink.internal.Platform; import jdk.tools.jlink.internal.ResourcePoolManager; import jdk.tools.jlink.internal.plugins.DefaultStripDebugPlugin; import jdk.tools.jlink.internal.plugins.DefaultStripDebugPlugin.NativePluginFactory; @@ -51,12 +53,23 @@ public void testWithNativeStripPresent() { DefaultStripDebugPlugin plugin = new DefaultStripDebugPlugin(javaPlugin, nativeFactory); ResourcePoolManager inManager = new ResourcePoolManager(); + inManager.add(ResourcePoolEntry.create(MockStripPlugin.DEBUGINFO_PATH, + ResourcePoolEntry.Type.NATIVE_LIB, new byte[]{0, 1, 2, 3})); + inManager.add(ResourcePoolEntry.create(MockStripPlugin.DIZ_PATH, + ResourcePoolEntry.Type.NATIVE_LIB, new byte[]{0, 1, 2, 3})); + ResourcePoolManager outManager = new ResourcePoolManager(); ResourcePool pool = plugin.transform(inManager.resourcePool(), - inManager.resourcePoolBuilder()); + outManager.resourcePoolBuilder()); if (!pool.findEntry(MockStripPlugin.JAVA_PATH).isPresent() || !pool.findEntry(MockStripPlugin.NATIVE_PATH).isPresent()) { throw new AssertionError("Expected both native and java to get called"); } + if (pool.findEntry(MockStripPlugin.DEBUGINFO_PATH).isPresent()) { + throw new AssertionError(".debuginfo file should have been excluded"); + } + if (pool.findEntry(MockStripPlugin.DIZ_PATH).isPresent()) { + throw new AssertionError(".diz file should have been excluded"); + } } public void testNoNativeStripPluginPresent() { @@ -66,11 +79,22 @@ public void testNoNativeStripPluginPresent() { DefaultStripDebugPlugin plugin = new DefaultStripDebugPlugin(javaPlugin, nativeFactory); ResourcePoolManager inManager = new ResourcePoolManager(); + inManager.add(ResourcePoolEntry.create(MockStripPlugin.DEBUGINFO_PATH, + ResourcePoolEntry.Type.NATIVE_LIB, new byte[]{0, 1, 2, 3})); + inManager.add(ResourcePoolEntry.create(MockStripPlugin.DIZ_PATH, + ResourcePoolEntry.Type.NATIVE_LIB, new byte[]{0, 1, 2, 3})); + ResourcePoolManager outManager = new ResourcePoolManager(); ResourcePool pool = plugin.transform(inManager.resourcePool(), - inManager.resourcePoolBuilder()); + outManager.resourcePoolBuilder()); if (!pool.findEntry(MockStripPlugin.JAVA_PATH).isPresent()) { throw new AssertionError("Expected java strip plugin to get called"); } + if (pool.findEntry(MockStripPlugin.DEBUGINFO_PATH).isPresent()) { + throw new AssertionError(".debuginfo file should have been excluded"); + } + if (pool.findEntry(MockStripPlugin.DIZ_PATH).isPresent()) { + throw new AssertionError(".diz file should have been excluded"); + } } // Disable embedded strip Java plugin, with native plugin present. @@ -84,12 +108,23 @@ public void testOnlyNativePlugin() { plugin.enableJavaStripPlugin(false); ResourcePoolManager inManager = new ResourcePoolManager(); + inManager.add(ResourcePoolEntry.create(MockStripPlugin.DEBUGINFO_PATH, + ResourcePoolEntry.Type.NATIVE_LIB, new byte[]{0, 1, 2, 3})); + inManager.add(ResourcePoolEntry.create(MockStripPlugin.DIZ_PATH, + ResourcePoolEntry.Type.NATIVE_LIB, new byte[]{0, 1, 2, 3})); + ResourcePoolManager outManager = new ResourcePoolManager(); ResourcePool pool = plugin.transform(inManager.resourcePool(), - inManager.resourcePoolBuilder()); + outManager.resourcePoolBuilder()); if (pool.findEntry(MockStripPlugin.JAVA_PATH).isPresent() || !pool.findEntry(MockStripPlugin.NATIVE_PATH).isPresent()) { throw new AssertionError("Expected only native to get called"); } + if (pool.findEntry(MockStripPlugin.DEBUGINFO_PATH).isPresent()) { + throw new AssertionError(".debuginfo file should have been excluded"); + } + if (pool.findEntry(MockStripPlugin.DIZ_PATH).isPresent()) { + throw new AssertionError(".diz file should have been excluded"); + } } // Disable embedded strip Java plugin, and without native plugin present. @@ -101,12 +136,23 @@ public void testNoOperation() { nativeFactory); plugin.enableJavaStripPlugin(false); ResourcePoolManager inManager = new ResourcePoolManager(); + inManager.add(ResourcePoolEntry.create(MockStripPlugin.DEBUGINFO_PATH, + ResourcePoolEntry.Type.NATIVE_LIB, new byte[]{0, 1, 2, 3})); + inManager.add(ResourcePoolEntry.create(MockStripPlugin.DIZ_PATH, + ResourcePoolEntry.Type.NATIVE_LIB, new byte[]{0, 1, 2, 3})); + ResourcePoolManager outManager = new ResourcePoolManager(); ResourcePool pool = plugin.transform(inManager.resourcePool(), - inManager.resourcePoolBuilder()); + outManager.resourcePoolBuilder()); if (pool.findEntry(MockStripPlugin.JAVA_PATH).isPresent() || pool.findEntry(MockStripPlugin.NATIVE_PATH).isPresent()) { throw new AssertionError("Expected both native and java not called"); } + if (pool.findEntry(MockStripPlugin.DEBUGINFO_PATH).isPresent()) { + throw new AssertionError(".debuginfo file should have been excluded"); + } + if (pool.findEntry(MockStripPlugin.DIZ_PATH).isPresent()) { + throw new AssertionError(".diz file should have been excluded"); + } } public static void main(String[] args) { @@ -121,10 +167,25 @@ public static class MockStripPlugin implements Plugin { private static final String NATIVE_PATH = "/foo/lib/test.so.debug"; private static final String JAVA_PATH = "/foo/TestClass.class"; + // Platform-appropriate debug file paths, matching the patterns chosen by + // DefaultStripDebugPlugin.debugFilePattern() for the runtime platform. + static final String DEBUGINFO_PATH = platformDebugPath(); + static final String DIZ_PATH = "/foo/lib/libfoo.diz"; private static final String STRIP_NATIVE_NAME = "strip-native-debug-symbols"; private static final String OMIT_ARG = "exclude-debuginfo-files"; private final boolean isNative; + private static String platformDebugPath() { + String platform = Platform.runtime().toString(); + if (platform.startsWith("windows")) { + return "/foo/bin/libfoo.pdb"; + } else if (platform.startsWith("macos")) { + return "/foo/lib/libfoo.dylib.dSYM/Contents/Resources/DWARF/libfoo.dylib"; + } else { + return "/foo/lib/libfoo.so.debuginfo"; + } + } + MockStripPlugin(boolean isNative) { this.isNative = isNative; } @@ -153,8 +214,7 @@ public ResourcePool transform(ResourcePool in, resPath = NATIVE_PATH; type = Type.NATIVE_LIB; } - ResourcePoolEntry entry = createMockEntry(resPath, type); - out.add(entry); + out.add(createMockEntry(resPath, type)); return out.build(); } diff --git a/test/jdk/tools/jlink/plugins/ExcludeJmodSectionPluginTest.java b/test/jdk/tools/jlink/plugins/ExcludeJmodSectionPluginTest.java index ac40cda036c7..1ee00d122185 100644 --- a/test/jdk/tools/jlink/plugins/ExcludeJmodSectionPluginTest.java +++ b/test/jdk/tools/jlink/plugins/ExcludeJmodSectionPluginTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ * @modules jdk.compiler * jdk.jlink * @build jdk.test.lib.compiler.CompilerUtils + * @build jdk.test.lib.util.FileUtils * @run testng ExcludeJmodSectionPluginTest */ @@ -35,12 +36,9 @@ import java.io.File; import java.io.IOException; import java.io.PrintWriter; -import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -49,7 +47,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.test.lib.compiler.CompilerUtils; +import jdk.test.lib.util.FileUtils; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -75,6 +76,8 @@ public class ExcludeJmodSectionPluginTest { static final Path INCLUDE_DIR = Paths.get("include"); static final Path IMAGES_DIR = Paths.get("images"); + private Path lastImageDir; + @BeforeTest private void setup() throws Exception { // build jmod files @@ -209,12 +212,13 @@ public void testJavaBase() { private Path createImage(String outputDir, List options, List expectedFiles) { + lastImageDir = IMAGES_DIR.resolve(outputDir); System.out.println("jlink " + options.toString()); int rc = JLINK_TOOL.run(System.out, System.out, options.toArray(new String[0])); assertTrue(rc == 0); - Path d = IMAGES_DIR.resolve(outputDir); + Path d = lastImageDir; for (String fn : expectedFiles) { Path path = d.resolve(fn); if (Files.notExists(path)) { @@ -224,24 +228,12 @@ private Path createImage(String outputDir, List options, return d; } - private void deleteDirectory(Path dir) throws IOException { - Files.walkFileTree(dir, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException - { - Files.delete(file); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) - throws IOException - { - Files.delete(dir); - return FileVisitResult.CONTINUE; - } - }); + @AfterMethod + public void cleanup(ITestResult result) throws IOException { + if (result.isSuccess() && lastImageDir != null && Files.exists(lastImageDir)) { + FileUtils.deleteFileTreeWithRetry(lastImageDir); + } + lastImageDir = null; } /** @@ -258,7 +250,7 @@ class JmodFileBuilder { Path msrc = SRC_DIR.resolve(name); if (Files.exists(msrc)) { - deleteDirectory(msrc); + FileUtils.deleteFileTreeWithRetry(msrc); } } diff --git a/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java b/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java index 998444b9a771..108664994473 100644 --- a/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java +++ b/test/jdk/tools/jlink/plugins/GenerateJLIClassesPluginTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,10 +34,13 @@ import java.util.List; import java.util.stream.Collectors; +import org.testng.ITestResult; import org.testng.Assert; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import jdk.test.lib.util.FileUtils; import jdk.tools.jlink.internal.LinkableRuntimeImage; import tests.Helper; import tests.JImageGenerator; @@ -48,7 +51,7 @@ /* * @test * @bug 8252919 8327499 - * @library ../../lib + * @library ../../lib /test/lib * @summary Test --generate-jli-classes plugin * @modules java.base/jdk.internal.jimage * jdk.jlink/jdk.tools.jlink.internal @@ -56,11 +59,13 @@ * jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jimage * @build tests.* + * @build jdk.test.lib.util.FileUtils * @run testng/othervm GenerateJLIClassesPluginTest */ public class GenerateJLIClassesPluginTest { private static Helper helper; + private static Path lastImageDir; @BeforeTest public static void setup() throws Exception { @@ -80,6 +85,21 @@ public static void setup() throws Exception { } } + @AfterMethod + public static void cleanup(ITestResult result) { + if (result.isSuccess() && lastImageDir != null && Files.exists(lastImageDir)) { + // On Windows, the java.exe process spawned by test methods may + // hold open handles to files even after completion. + // Use a non-throwing cleanup method but tolerate failures. Just log them. + List failures = FileUtils.deleteFileTreeUnchecked(lastImageDir); + if (!failures.isEmpty()) { + System.err.println("WARNING: cleanup of " + lastImageDir + " incomplete:"); + failures.forEach(e -> System.err.println(" " + e)); + } + } + lastImageDir = null; + } + @Test public static void testSpecies() throws IOException { // Check that --generate-jli-classes=@file works as intended @@ -88,7 +108,7 @@ public static void testSpecies() throws IOException { String fileString = "[SPECIES_RESOLVE] java.lang.invoke.BoundMethodHandle$Species_" + species + " (salvaged)\n"; Files.write(baseFile, fileString.getBytes(Charset.defaultCharset())); Result result = JImageGenerator.getJLinkTask() - .output(helper.createNewImageDir("generate-jli-file")) + .output(lastImageDir = helper.createNewImageDir("generate-jli-file")) .option("--generate-jli-classes=@" + baseFile.toString()) .addMods("java.base") .call(); @@ -113,7 +133,7 @@ public static void testInvalidSignatures() throws IOException { fileString = "[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeVirtual L_L (success)\n"; Files.write(failFile, fileString.getBytes(Charset.defaultCharset())); Result result = JImageGenerator.getJLinkTask() - .output(helper.createNewImageDir("invalid-signature")) + .output(lastImageDir = helper.createNewImageDir("invalid-signature")) .option("--generate-jli-classes=@" + failFile.toString()) .addMods("java.base") .call(); @@ -125,7 +145,7 @@ public static void testInvalidSignatures() throws IOException { @Test public static void nonExistentTraceFile() throws IOException { Result result = JImageGenerator.getJLinkTask() - .output(helper.createNewImageDir("non-existent-tracefile")) + .output(lastImageDir = helper.createNewImageDir("non-existent-tracefile")) .option("--generate-jli-classes=@NON_EXISTENT_FILE") .addMods("java.base") .call(); @@ -140,7 +160,7 @@ public static void testInvokers() throws IOException { Path invokersTrace = Files.createTempFile("invokers", "trace"); Files.writeString(invokersTrace, fileString, Charset.defaultCharset()); Result result = JImageGenerator.getJLinkTask() - .output(helper.createNewImageDir("jli-invokers")) + .output(lastImageDir = helper.createNewImageDir("jli-invokers")) .option("--generate-jli-classes=@" + invokersTrace.toString()) .addMods("java.base") .call(); diff --git a/test/jdk/tools/jlink/plugins/IncludeLocalesPluginTest.java b/test/jdk/tools/jlink/plugins/IncludeLocalesPluginTest.java index 008b4a175a93..3ab980a3b0fa 100644 --- a/test/jdk/tools/jlink/plugins/IncludeLocalesPluginTest.java +++ b/test/jdk/tools/jlink/plugins/IncludeLocalesPluginTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ */ import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -35,11 +36,13 @@ import jdk.tools.jlink.internal.plugins.PluginsResourceBundle; import jdk.tools.jlink.plugin.PluginException; import jdk.test.lib.Platform; +import jdk.test.lib.util.FileUtils; import tests.Helper; import tests.JImageGenerator; import tests.JImageValidator; import tests.Result; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -67,6 +70,7 @@ * jdk.compiler * @build tests.* * @build jdk.test.lib.Platform + * @build jdk.test.lib.util.FileUtils * @build tools.jlink.plugins.GetAvailableLocales * @run junit/othervm/timeout=180 -Xmx1g IncludeLocalesPluginTest */ @@ -75,6 +79,8 @@ public class IncludeLocalesPluginTest { private static final String MODULE_NAME = "IncludeLocalesTest"; private static Helper helper; + private Path lastImageDir; + private boolean testPassed; // Test data should include: // - --include-locales command line option @@ -422,18 +428,22 @@ public static void setup() throws IOException { @MethodSource("testData") public void launch(String optIncludeLocales, String optAddModules, List requiredRes, List shouldNotExistRes, List availableLocs, String errorMsg) throws Exception { + testPassed = false; // create image for each test data Result result; + lastImageDir = helper.createNewImageDir(MODULE_NAME); if (optIncludeLocales.isEmpty()) { System.out.println("Invoking jlink with no --include-locales option"); result = JImageGenerator.getJLinkTask() - .output(helper.createNewImageDir(MODULE_NAME)) + .output(lastImageDir) + .option("--strip-debug") .addMods(optAddModules) .call(); } else { System.out.println("Invoking jlink with \"" + optIncludeLocales + "\""); result = JImageGenerator.getJLinkTask() - .output(helper.createNewImageDir(MODULE_NAME)) + .output(lastImageDir) + .option("--strip-debug") .addMods(optAddModules) .option(optIncludeLocales) .call(); @@ -452,6 +462,21 @@ public void launch(String optIncludeLocales, String optAddModules, List .getMessage("error.prefix") + " " +errorMsg); System.out.println("\tExpected failure: " + result.getMessage()); } + testPassed = true; + } + + @AfterEach + public void cleanup() { + if (testPassed && lastImageDir != null && Files.exists(lastImageDir)) { + // On Windows, the java.exe process spawned by testAvailableLocales() may + // hold open handles to files even after waitFor() returns. + // Use a non-throwing cleanup method but tolerate failures. Just log them. + List failures = FileUtils.deleteFileTreeUnchecked(lastImageDir); + if (!failures.isEmpty()) { + System.err.println("WARNING: cleanup of " + lastImageDir + " incomplete:"); + failures.forEach(e -> System.err.println(" " + e)); + } + } } private static void testLocaleDataEntries(Path image, List expectedLocations, diff --git a/test/jdk/tools/jlink/plugins/LegalFilePluginTest.java b/test/jdk/tools/jlink/plugins/LegalFilePluginTest.java index 3647e0fb27bf..07dd1d514be8 100644 --- a/test/jdk/tools/jlink/plugins/LegalFilePluginTest.java +++ b/test/jdk/tools/jlink/plugins/LegalFilePluginTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @modules jdk.compiler * jdk.jlink * @build jdk.test.lib.compiler.CompilerUtils + * @build jdk.test.lib.util.FileUtils * @run testng LegalFilePluginTest */ @@ -38,12 +39,9 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.io.UncheckedIOException; -import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -54,7 +52,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.test.lib.compiler.CompilerUtils; +import jdk.test.lib.util.FileUtils; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -79,6 +80,8 @@ public class LegalFilePluginTest { static final Path LEGAL_DIR = Paths.get("legal"); static final Path IMAGES_DIR = Paths.get("images"); + private Path lastImageDir; + static final Map, Map> LICENSES = Map.of( // Key is module name and requires // Value is a map of filename to the file content @@ -259,32 +262,21 @@ private void compareFileContent(Path file, String content) { } private Path createImage(String outputDir, List options) { + lastImageDir = IMAGES_DIR.resolve(outputDir); System.out.println("jlink " + options.stream().collect(Collectors.joining(" "))); int rc = JLINK_TOOL.run(System.out, System.out, options.toArray(new String[0])); assertTrue(rc == 0); - return IMAGES_DIR.resolve(outputDir); + return lastImageDir; } - private void deleteDirectory(Path dir) throws IOException { - Files.walkFileTree(dir, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException - { - Files.delete(file); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) - throws IOException - { - Files.delete(dir); - return FileVisitResult.CONTINUE; - } - }); + @AfterMethod + public void cleanup(ITestResult result) throws IOException { + if (result.isSuccess() && lastImageDir != null && Files.exists(lastImageDir)) { + FileUtils.deleteFileTreeWithRetry(lastImageDir); + } + lastImageDir = null; } /** @@ -301,7 +293,7 @@ class JmodFileBuilder { Path msrc = SRC_DIR.resolve(name); if (Files.exists(msrc)) { - deleteDirectory(msrc); + FileUtils.deleteFileTreeWithRetry(msrc); } } diff --git a/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java index b7c9a59bd655..6d7e4df7fabc 100644 --- a/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java +++ b/test/jdk/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,9 @@ import static jdk.test.lib.process.ProcessTools.*; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import static org.testng.Assert.*; @@ -68,6 +71,8 @@ public class UserModuleTest { // the names of the modules in this test private static String[] modules = new String[] {"m1", "m2", "m3", "m4", "m5"}; + private Path lastPerTestImageDir; + private static boolean hasJmods() { if (!Files.exists(Paths.get(JAVA_HOME, "jmods"))) { @@ -100,6 +105,22 @@ public void compileAll() throws Throwable { createJmods("m1", "m4"); } + @AfterMethod + public void cleanupPerTestImage(ITestResult result) throws IOException { + if (result.isSuccess() && lastPerTestImageDir != null + && Files.exists(lastPerTestImageDir)) { + FileUtils.deleteFileTreeWithRetry(lastPerTestImageDir); + } + lastPerTestImageDir = null; + } + + @AfterTest(alwaysRun = true) + public void cleanupSharedImage() throws IOException { + if (Files.exists(IMAGE)) { + FileUtils.deleteFileTreeWithRetry(IMAGE); + } + } + /* * Test the image created when linking with a module with * no Packages attribute @@ -157,7 +178,7 @@ public void disableSystemModules() throws Throwable { public void testDedupSet() throws Throwable { if (!hasJmods()) return; - Path dir = Paths.get("dedupSetTest"); + Path dir = lastPerTestImageDir = Paths.get("dedupSetTest"); createImage(dir, "m1", "m2", "m3", "m4"); Path java = dir.resolve("bin").resolve("java"); assertTrue(executeProcess(java.toString(), @@ -172,7 +193,7 @@ public void testDedupSet() throws Throwable { public void testRequiresStatic() throws Throwable { if (!hasJmods()) return; - Path dir = Paths.get("requiresStatic"); + Path dir = lastPerTestImageDir = Paths.get("requiresStatic"); createImage(dir, "m5"); Path java = dir.resolve("bin").resolve("java"); assertTrue(executeProcess(java.toString(), "-m", "m5/p5.Main") @@ -194,7 +215,7 @@ public void testRequiresStatic() throws Throwable { public void testRequiresStatic2() throws Throwable { if (!hasJmods()) return; - Path dir = Paths.get("requiresStatic2"); + Path dir = lastPerTestImageDir = Paths.get("requiresStatic2"); createImage(dir, "m3", "m5"); Path java = dir.resolve("bin").resolve("java"); @@ -242,7 +263,7 @@ public void testModulePackagesAttribute() throws Throwable { if (!hasJmods()) return; // create an image using JMOD files - Path dir = Paths.get("packagesTest"); + Path dir = lastPerTestImageDir = Paths.get("packagesTest"); String mp = Paths.get(JAVA_HOME, "jmods").toString() + File.pathSeparator + JMODS_DIR.toString(); @@ -271,7 +292,7 @@ public void testRetainModuleTarget() throws Throwable { if (!hasJmods()) return; // create an image using JMOD files - Path dir = Paths.get("retainModuleTargetTest"); + Path dir = lastPerTestImageDir = Paths.get("retainModuleTargetTest"); String mp = Paths.get(JAVA_HOME, "jmods").toString() + File.pathSeparator + JMODS_DIR.toString(); diff --git a/test/jdk/tools/jlink/runtimeImage/AbstractLinkableRuntimeTest.java b/test/jdk/tools/jlink/runtimeImage/AbstractLinkableRuntimeTest.java index 1df4455bc7dc..9414508769bb 100644 --- a/test/jdk/tools/jlink/runtimeImage/AbstractLinkableRuntimeTest.java +++ b/test/jdk/tools/jlink/runtimeImage/AbstractLinkableRuntimeTest.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2024, Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -41,6 +42,7 @@ import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.util.FileUtils; import jdk.tools.jlink.internal.LinkableRuntimeImage; import tests.Helper; import tests.JImageGenerator; @@ -141,7 +143,11 @@ protected Path createJavaImageRuntimeLink(BaseJlinkSpec baseSpec, Set ex for (String extra: baseSpec.getExtraOptions()) { builder.extraJlinkOpt(extra); } - return jlinkUsingImage(builder.build()); + Path finalImage = jlinkUsingImage(builder.build()); + // The intermediate run-time link image was only needed as the jlink + // source for producing the final image; free the disk space now. + FileUtils.deleteFileTreeWithRetry(runtimeJlinkImage); + return finalImage; } protected Path jlinkUsingImage(JlinkSpec spec) throws Exception { @@ -280,6 +286,12 @@ protected Path createRuntimeLinkImage(BaseJlinkSpec baseSpec, // Remove JMODs as needed for the test copyJDKTreeWithoutSpecificJmods(from, runtimeJlinkImage, excludedJmodFiles); + // In the non-linkable-runtime case 'from' is a temporary + // --generate-linkable-runtime image that has now been copied into + // 'runtimeJlinkImage'; delete it to free the disk space. + if (!baseSpec.isLinkableRuntime()) { + FileUtils.deleteFileTreeWithRetry(from); + } // Verify the base image is actually without desired packaged modules if (excludedJmodFiles.isEmpty()) { if (Files.exists(runtimeJlinkImage.resolve("jmods"))) {