blob: 90846ad6c11b16ba2ab7cd2229420cb749d68f49 [file] [log] [blame]
// 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
}