This is an automated email from the ASF dual-hosted git repository.

XiaoHongbo-Hope pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-mosaic.git


The following commit(s) were added to refs/heads/main by this push:
     new f38fb7f  fix(release): include third-party licenses in binary LICENSE 
files (#101)
f38fb7f is described below

commit f38fb7f9e1c94865f4318fa6f620a538773c9bad
Author: jianguotian <[email protected]>
AuthorDate: Wed Oct 21 10:16:14 2026 +0800

    fix(release): include third-party licenses in binary LICENSE files (#101)
    
    * fix(release): include third-party licenses in binary LICENSE files
    
    * fix(release): narrow RC verification changes
    
    * docs(release): keep RC instructions focused
    
    ---------
    
    Co-authored-by: mingfeng <[email protected]>
---
 docs/verifying-a-release-candidate.html         |   6 +-
 java/pom.xml                                    |   1 +
 java/src/main/binary-resources/META-INF/LICENSE | 438 +++++++++++++++++++++++-
 python/setup.py                                 |  12 +-
 python/tests/test_packaging.py                  |   7 +-
 tools/generate_license_reports.py               |  12 +-
 tools/tests/test_generate_license_reports.py    |  18 +
 tools/tests/test_verify_binary_artifact.py      | 129 +++++++
 tools/verify_binary_artifact.py                 |  24 +-
 9 files changed, 620 insertions(+), 27 deletions(-)

diff --git a/docs/verifying-a-release-candidate.html 
b/docs/verifying-a-release-candidate.html
index fb1d44f..f003408 100644
--- a/docs/verifying-a-release-candidate.html
+++ b/docs/verifying-a-release-candidate.html
@@ -209,8 +209,10 @@ paimon-mosaic-core = { git = 
"https://github.com/apache/paimon-mosaic";, tag = "v
             </ol>
 
             <h3>Python (TestPyPI)</h3>
-            <p>The RC tag publishes wheels to TestPyPI after the Rust and Java 
release jobs succeed. Install and verify:</p>
-<pre><code>pip install -i https://test.pypi.org/simple/ 
paimon-mosaic==${RELEASE_VERSION}rc${RC_NUM}
+            <p>The RC tag publishes wheels to TestPyPI after the Rust and Java 
release jobs succeed. In a fresh virtual environment, install dependencies from 
PyPI first, then install the exact RC wheel from TestPyPI without resolving 
dependencies there:</p>
+<pre><code>python -m pip install --index-url https://pypi.org/simple/ pyarrow
+python -m pip install --no-deps --index-url https://test.pypi.org/simple/ 
paimon-mosaic==${RELEASE_VERSION}rc${RC_NUM}
+python -m pip check
 python -c "import mosaic; print('OK')"</code></pre>
             <p>Verify wheels are available for: Linux x86_64, Linux aarch64, 
macOS aarch64, Windows x86_64.</p>
 
diff --git a/java/pom.xml b/java/pom.xml
index 438ac23..cff0f76 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -141,6 +141,7 @@
                                             
<directory>src/main/binary-resources</directory>
                                             <filtering>false</filtering>
                                             <includes>
+                                                
<include>META-INF/LICENSE</include>
                                                 
<include>META-INF/NOTICE</include>
                                             </includes>
                                         </resource>
diff --git a/java/src/main/binary-resources/META-INF/LICENSE 
b/java/src/main/binary-resources/META-INF/LICENSE
index ffcce7d..215ba9d 100644
--- a/java/src/main/binary-resources/META-INF/LICENSE
+++ b/java/src/main/binary-resources/META-INF/LICENSE
@@ -200,15 +200,429 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 
-===============================================================================
-BUNDLED THIRD-PARTY COMPONENTS
-===============================================================================
-
-This binary JAR bundles Rust native libraries for four release targets.
-The component inventory, copyright notices, and complete license texts
-are provided in:
-
-    META-INF/licenses/x86_64-unknown-linux-gnu/THIRD-PARTY-LICENSES.html
-    META-INF/licenses/aarch64-unknown-linux-gnu/THIRD-PARTY-LICENSES.html
-    META-INF/licenses/aarch64-apple-darwin/THIRD-PARTY-LICENSES.html
-    META-INF/licenses/x86_64-pc-windows-msvc/THIRD-PARTY-LICENSES.html
+Apache Paimon Mosaic Java binary distributions
+===============================================
+
+The Java JAR includes the compiled paimon-mosaic-jni Rust dependency closure
+and the Rust standard library.
+
+Except for the components listed below, Apache Paimon Mosaic selects the
+Apache License, Version 2.0 option for dependencies that offer it. The full
+Apache License, Version 2.0 text is in LICENSE.
+
+arrow-array 58.4.0
+------------------
+
+This component is distributed under both the Apache License, Version 2.0 and
+the following MIT license.
+
+MIT License
+
+Copyright (c) 2020-2022 Oliver Margetts
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+bytes 1.12.1
+------------
+
+Copyright (c) 2018 Carl Lerche
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+combine 4.6.7
+-------------
+
+The MIT License (MIT)
+
+Copyright (c) 2015 Markus Westerlind
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+crunchy 0.2.4
+-------------
+
+The MIT License (MIT)
+
+Copyright 2017-2023 Eira Fransham.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+libm 0.2.16
+-----------
+
+This component is distributed under the following MIT license option.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+This Rust library contains the following copyrights:
+
+    Copyright (c) 2018 Jorge Aparicio
+
+Portions of this software are derived from third-party works licensed under
+terms compatible with the above MIT license:
+
+* musl libc https://www.musl-libc.org/. This library contains the following
+  copyright:
+
+      Copyright © 2005-2020 Rich Felker, et al.
+
+* The CORE-MATH project https://core-math.gitlabpages.inria.fr/. CORE-MATH
+  routines are available under the MIT license on a per-file basis.
+
+The musl libc COPYRIGHT file also includes the following notice relevant to
+math portions of the library:
+
+Much of the math library code (src/math/* and src/complex/*) is
+Copyright © 1993,2004 Sun Microsystems or
+Copyright © 2003-2011 David Schultz or
+Copyright © 2003-2009 Steven G. Kargl or
+Copyright © 2003-2009 Bruce D. Evans or
+Copyright © 2008 Stephen L. Moshier or
+Copyright © 2017-2018 Arm Limited
+and labelled as such in comments in the individual source files. All
+have been licensed under extremely permissive terms.
+
+Copyright notices are retained in src/* files where relevant.
+
+memchr 2.8.3
+------------
+
+This component is distributed under the following MIT license option.
+
+The MIT License (MIT)
+
+Copyright (c) 2015 Andrew Gallant
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+tiny-keccak 2.0.2
+-----------------
+
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
+
+unicode-ident 1.0.24 and Rust standard library 1.97.1
+---------------------------------------------------
+
+The Rust code is distributed under the Apache License, Version 2.0 option.
+Unicode data from unicode-ident has Copyright © 1991-2023 Unicode, Inc.
+Unicode data from the Rust standard library has
+Copyright © 1991-2024 Unicode, Inc. Both are additionally covered by the
+following Unicode License V3.
+
+UNICODE LICENSE V3
+
+COPYRIGHT AND PERMISSION NOTICE
+
+NOTICE TO USER: Carefully read the following legal agreement. BY
+DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
+SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
+TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
+DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of data files and any associated documentation (the "Data Files") or
+software and any associated documentation (the "Software") to deal in the
+Data Files or Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, and/or sell
+copies of the Data Files or Software, and to permit persons to whom the
+Data Files or Software are furnished to do so, provided that either (a)
+this copyright and permission notice appear with all copies of the Data
+Files or Software, or (b) this copyright and permission notice appear in
+associated Documentation.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+THIRD PARTY RIGHTS.
+
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
+BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
+OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
+FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or other
+dealings in these Data Files or Software without prior written
+authorization of the copyright holder.
+
+zstd 0.13.3
+-----------
+
+The MIT License (MIT)
+Copyright (c) 2016 Alexandre Bury
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+zstd-sys 2.0.16+zstd.1.5.7 (vendored Zstandard 1.5.7)
+-----------------------------------------------------
+
+BSD License
+
+For Zstandard software
+
+Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+ * Neither the name Facebook, nor Meta, nor the names of its contributors may
+   be used to endorse or promote products derived from this software without
+   specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 
FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/python/setup.py b/python/setup.py
index 8180fdf..7438653 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -73,9 +73,14 @@ def _stage_legal_files(destination_dir):
             source = os.path.join(here, name)
         if not os.path.isfile(source):
             raise RuntimeError(f"required binary legal file is missing: 
{source}")
+        with open(source, "rb") as source_file:
+            source_bytes = source_file.read()
+        if name == "LICENSE" and source == os.path.join(here, "../LICENSE"):
+            # Build the primary binary LICENSE from the maintained inventory.
+            # An sdist already carries this combined file; do not append twice.
+            with open(os.path.join(here, "../LICENSE-binary-ffi"), "rb") as 
licenses:
+                source_bytes = source_bytes.rstrip() + b"\n\n" + 
licenses.read()
         if os.path.exists(destination):
-            with open(source, "rb") as source_file:
-                source_bytes = source_file.read()
             with open(destination, "rb") as destination_file:
                 destination_bytes = destination_file.read()
             if source_bytes != destination_bytes:
@@ -83,7 +88,8 @@ def _stage_legal_files(destination_dir):
                     f"staged binary legal file does not match source: 
{destination}"
                 )
             continue
-        shutil.copy2(source, destination)
+        with open(destination, "wb") as destination_file:
+            destination_file.write(source_bytes)
         staged.append(destination)
 
     return staged
diff --git a/python/tests/test_packaging.py b/python/tests/test_packaging.py
index bd2904f..e7f5980 100644
--- a/python/tests/test_packaging.py
+++ b/python/tests/test_packaging.py
@@ -63,8 +63,13 @@ def 
test_sdist_to_wheel_preserves_canonical_legal_files(tmp_path):
     with tarfile.open(sdist) as archive:
         prefix = sdist.name.removesuffix(".tar.gz")
         for name, source in legal_sources.items():
+            expected = (ROOT / source).read_bytes()
+            if name == "LICENSE":
+                expected = expected.rstrip() + b"\n\n" + (
+                    ROOT / "LICENSE-binary-ffi"
+                ).read_bytes()
             with archive.extractfile(f"{prefix}/{name}") as legal_file:
-                assert legal_file.read() == (ROOT / source).read_bytes()
+                assert legal_file.read() == expected
 
     wheel, = (package / "dist").glob("*.whl")
     result = subprocess.run(
diff --git a/tools/generate_license_reports.py 
b/tools/generate_license_reports.py
index d7c5d6a..6e02908 100755
--- a/tools/generate_license_reports.py
+++ b/tools/generate_license_reports.py
@@ -440,14 +440,10 @@ def generated_files(root: Path) -> dict[Path, str]:
     apache_license = (root / "LICENSE").read_text(encoding="utf-8")
     project_notice = (root / "NOTICE").read_text(encoding="utf-8")
 
-    java_report_paths = [
-        f"META-INF/licenses/{target}/THIRD-PARTY-LICENSES.html"
-        for target in TARGETS
-    ]
-    java_license = binary_license(
-        apache_license,
-        "This binary JAR bundles Rust native libraries for four release 
targets.",
-        java_report_paths,
+    java_license = (
+        apache_license.rstrip()
+        + "\n\n"
+        + (root / "LICENSE-binary").read_text(encoding="utf-8")
     )
     result[
         root / "java/src/main/binary-resources/META-INF/LICENSE"
diff --git a/tools/tests/test_generate_license_reports.py 
b/tools/tests/test_generate_license_reports.py
index cf789f9..febb1fd 100644
--- a/tools/tests/test_generate_license_reports.py
+++ b/tools/tests/test_generate_license_reports.py
@@ -67,3 +67,21 @@ def test_generate_removes_obsolete_target_report(tmp_path: 
Path) -> None:
 
     assert expected.read_text(encoding="utf-8") == "current\n"
     assert not obsolete.exists()
+
+
+def test_java_license_contains_the_packaged_inventory(tmp_path, monkeypatch):
+    (tmp_path / "LICENSE").write_text("Apache license\n", encoding="utf-8")
+    (tmp_path / "LICENSE-binary").write_text(
+        "Bundled JNI component license\n", encoding="utf-8"
+    )
+    (tmp_path / "NOTICE").write_text("Project notice\n", encoding="utf-8")
+    monkeypatch.setattr(generator, "verify_cargo_about", lambda root: None)
+    monkeypatch.setattr(generator, "generate_base_report", lambda *args: 
"report")
+    monkeypatch.setattr(generator, "cargo_metadata", lambda *args: {})
+    monkeypatch.setattr(generator, "third_party_notices", lambda *args: ())
+    monkeypatch.setattr(generator, "complete_report", lambda *args: "report")
+
+    files = generator.generated_files(tmp_path)
+    assert files[tmp_path / "java/src/main/binary-resources/META-INF/LICENSE"] 
== (
+        "Apache license\n\nBundled JNI component license\n"
+    )
diff --git a/tools/tests/test_verify_binary_artifact.py 
b/tools/tests/test_verify_binary_artifact.py
new file mode 100644
index 0000000..deb54bc
--- /dev/null
+++ b/tools/tests/test_verify_binary_artifact.py
@@ -0,0 +1,129 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from pathlib import Path
+import sys
+import zipfile
+
+import pytest
+
+
+sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
+import verify_binary_artifact as verifier  # noqa: E402
+
+
[email protected]
+def repository(tmp_path, monkeypatch):
+    root = tmp_path / "repository"
+    root.mkdir()
+    for name, content in {
+        "LICENSE": b"Apache license\n",
+        "LICENSE-binary": b"JNI third-party licenses\n",
+        "LICENSE-binary-ffi": b"FFI third-party licenses\n",
+        "java/src/main/binary-resources/META-INF/NOTICE": b"Binary notices\n",
+    }.items():
+        path = root / name
+        path.parent.mkdir(parents=True, exist_ok=True)
+        path.write_bytes(content)
+    monkeypatch.setattr(verifier, "ROOT", root)
+    return root
+
+
+def jar_entries():
+    return {
+        "META-INF/LICENSE": b"Apache license\n\nJNI third-party licenses\n",
+        "META-INF/LICENSE-binary": b"JNI third-party licenses\n",
+        "META-INF/NOTICE": b"Binary notices\n",
+        "META-INF/DEPENDENCIES": b"Dependencies\n",
+        "org/apache/paimon/mosaic/NativeLib.class": b"class",
+        **{name: b"native" for name in verifier.JAR_NATIVE_FILES},
+    }
+
+
+def wheel_entries():
+    return {
+        "mosaic-0.3.0.dist-info/METADATA": b"Name: paimon-mosaic\nVersion: 
0.3.0\n",
+        "mosaic/libpaimon_mosaic_ffi.so": b"native",
+        **{
+            f"{prefix}/{name}": content
+            for prefix in ("mosaic", "mosaic-0.3.0.dist-info/licenses")
+            for name, content in {
+                "LICENSE": b"Apache license\n\nFFI third-party licenses\n",
+                "LICENSE-binary": b"FFI third-party licenses\n",
+                "NOTICE": b"Binary notices\n",
+            }.items()
+        },
+    }
+
+
+def verify_archive(tmp_path, kind, entries):
+    artifact = tmp_path / f"artifact.{kind}"
+    with zipfile.ZipFile(artifact, "w") as archive:
+        for name, content in entries.items():
+            archive.writestr(name, content)
+    if kind == "jar":
+        verifier.verify_jar(artifact, verifier.JAR_NATIVE_FILES)
+    else:
+        verifier.verify_wheel(artifact)
+
+
[email protected]("kind,entries", [("jar", jar_entries()), ("whl", 
wheel_entries())])
+def test_accepts_complete_binary_license(repository, tmp_path, kind, entries):
+    verify_archive(tmp_path, kind, entries)
+
+
[email protected](
+    "kind,entry",
+    [
+        ("jar", "META-INF/LICENSE"),
+        ("whl", "mosaic/LICENSE"),
+        ("whl", "mosaic-0.3.0.dist-info/licenses/LICENSE"),
+    ],
+)
[email protected](
+    "license_content",
+    [
+        b"Apache license\n",
+        b"Apache license\n\nSee missing/THIRD-PARTY-LICENSES.html\n",
+        b"Apache license\n\nOutdated third-party inventory\n",
+    ],
+)
+def test_rejects_incomplete_primary_license(
+    repository, tmp_path, kind, entry, license_content
+):
+    entries = jar_entries() if kind == "jar" else wheel_entries()
+    entries[entry] = license_content
+    with pytest.raises(ValueError, match="complete third-party license 
inventory"):
+        verify_archive(tmp_path, kind, entries)
+
+
[email protected](
+    "kind,entry",
+    [("jar", "META-INF/LICENSE-binary"), ("whl", "mosaic/LICENSE-binary")],
+)
+def test_still_rejects_missing_inventory(repository, tmp_path, kind, entry):
+    entries = jar_entries() if kind == "jar" else wheel_entries()
+    del entries[entry]
+    with pytest.raises(ValueError, match="missing entries"):
+        verify_archive(tmp_path, kind, entries)
+
+
+def test_rejects_stale_java_license_before_resolving_dependencies(repository):
+    path = repository / "java/src/main/binary-resources/META-INF/LICENSE"
+    path.write_bytes(b"Apache license\n")
+    with pytest.raises(ValueError, match="Java binary LICENSE is stale"):
+        verifier.verify_source_legal_inventory()
diff --git a/tools/verify_binary_artifact.py b/tools/verify_binary_artifact.py
index e3685a9..bde4096 100644
--- a/tools/verify_binary_artifact.py
+++ b/tools/verify_binary_artifact.py
@@ -31,7 +31,6 @@ from pathlib import Path
 ROOT = Path(__file__).resolve().parent.parent
 LEGAL_FILES = ("LICENSE", "NOTICE", "LICENSE-binary")
 WHEEL_LEGAL_SOURCES = {
-    "LICENSE": "LICENSE",
     "NOTICE": "java/src/main/binary-resources/META-INF/NOTICE",
     "LICENSE-binary": "LICENSE-binary-ffi",
 }
@@ -79,6 +78,20 @@ def require_canonical_content(archive, artifact, 
archive_prefix, sources):
             raise ValueError(f"{artifact}!/{entry} does not match repository 
{source}")
 
 
+def binary_license_content(inventory):
+    return (ROOT / "LICENSE").read_bytes().rstrip() + b"\n\n" + (
+        ROOT / inventory
+    ).read_bytes()
+
+
+def require_binary_license(archive, artifact, entry, inventory):
+    if archive.read(entry) != binary_license_content(inventory):
+        raise ValueError(
+            f"{artifact}!/{entry} must contain the Apache license and the "
+            f"complete third-party license inventory from {inventory}"
+        )
+
+
 def verify_wheel(artifact):
     with zipfile.ZipFile(artifact) as archive:
         names = archive.namelist()
@@ -91,6 +104,8 @@ def verify_wheel(artifact):
         dist_legal = [f"{dist_info}licenses/{name}" for name in LEGAL_FILES]
         package_legal = [f"mosaic/{name}" for name in LEGAL_FILES]
         require_entries(archive, artifact, dist_legal + package_legal)
+        for entry in ("mosaic/LICENSE", f"{dist_info}licenses/LICENSE"):
+            require_binary_license(archive, artifact, entry, 
"LICENSE-binary-ffi")
         require_canonical_content(
             archive, artifact, "mosaic", WHEEL_LEGAL_SOURCES
         )
@@ -126,6 +141,7 @@ def verify_jar(artifact, native_files):
             "org/apache/paimon/mosaic/NativeLib.class",
         ) + tuple(native_files)
         require_entries(archive, artifact, required)
+        require_binary_license(archive, artifact, "META-INF/LICENSE", 
"LICENSE-binary")
         require_canonical_content(
             archive,
             artifact,
@@ -187,6 +203,12 @@ def resolved_runtime_dependencies(metadata, root_name):
 
 
 def verify_source_legal_inventory():
+    java_license = ROOT / "java/src/main/binary-resources/META-INF/LICENSE"
+    if java_license.read_bytes() != binary_license_content("LICENSE-binary"):
+        raise ValueError(
+            "Java binary LICENSE is stale: regenerate it from LICENSE and "
+            "LICENSE-binary with tools/generate_license_reports.py"
+        )
     metadata_by_target = [cargo_metadata(target) for target in 
SUPPORTED_TARGETS]
     for root_name, license_name in (
         ("paimon-mosaic-ffi", "LICENSE-binary-ffi"),

Reply via email to