| // 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. | |
| package main | |
| import ( | |
| "archive/zip" | |
| "fmt" | |
| "io" | |
| "os" | |
| "strings" | |
| ) | |
| // makePathingJar produces a context or 'pathing' jar which only contains the relative | |
| // classpaths in its META-INF/MANIFEST.MF. | |
| // | |
| // Since we build with Java 8 as a minimum, this is the only supported way of reducing | |
| // command line length, since argsfile support wasn't added until Java 9. | |
| // | |
| // https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html is the spec. | |
| // | |
| // In particular, we only need to populate the Jar with a Manifest-Version and Class-Path | |
| // attributes. | |
| // Per https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#classpath | |
| // the classpath URLs must be relative for security reasons. | |
| func makePathingJar(classpaths []string) (string, error) { | |
| f, err := os.Create("pathing.jar") | |
| if err != nil { | |
| return "", fmt.Errorf("unable to create pathing.jar: %w", err) | |
| } | |
| defer f.Close() | |
| if err := writePathingJar(classpaths, f); err != nil { | |
| return "", fmt.Errorf("unable to write pathing.jar: %w", err) | |
| } | |
| return f.Name(), nil | |
| } | |
| var lineBreak = []byte{'\r', '\n'} | |
| var continuation = []byte{' '} | |
| func writePathingJar(classpaths []string, w io.Writer) error { | |
| jar := zip.NewWriter(w) | |
| defer jar.Close() | |
| if _, err := jar.Create("META-INF/"); err != nil { | |
| return fmt.Errorf("unable to create META-INF/ directory: %w", err) | |
| } | |
| zf, err := jar.Create("META-INF/MANIFEST.MF") | |
| if err != nil { | |
| return fmt.Errorf("unable to create META-INF/MANIFEST.MF: %w", err) | |
| } | |
| zf.Write([]byte("Manifest-Version: 1.0")) | |
| zf.Write(lineBreak) | |
| zf.Write([]byte("Created-By: sdks/java/container/pathingjar.go")) | |
| zf.Write(lineBreak) | |
| // Class-Path: must have a sequence of relative URIs for the paths | |
| // which we assume outright in this case. | |
| // We could do this memory efficiently, but it's not worthwhile compared to the complexity | |
| // at this stage. | |
| allCPs := "Class-Path: file:" + strings.Join(classpaths, " file:") | |
| const lineLenMax = 71 // it's actually 72, but we remove 1 to account for the continuation line prefix. | |
| buf := make([]byte, lineLenMax) | |
| cur := 0 | |
| for cur+lineLenMax < len(allCPs) { | |
| next := cur + lineLenMax | |
| copy(buf, allCPs[cur:next]) | |
| zf.Write(buf) | |
| zf.Write(lineBreak) | |
| zf.Write(continuation) | |
| cur = next | |
| } | |
| zf.Write([]byte(allCPs[cur:])) | |
| zf.Write(lineBreak) | |
| return nil | |
| } |