blob: 96d6c5c3b735b4114998767655a8cec20da4c2af [file] [log] [blame]
// Copyright 2024 The Bazel Authors. All rights reserved.
//
// Licensed 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 extractresources extracts resources from a jar and put them into a separate zip file.
package extractresources
import (
"archive/zip"
"io"
"os"
"slices"
"strings"
"sync"
"log"
"src/tools/ak/types"
)
var (
// Cmd defines the command.
Cmd = types.Command{
Init: Init,
Run: Run,
Desc: desc,
}
initOnce sync.Once
excludedExtensions = []string{
".aidl", // Android interface definition files
".rs", // RenderScript files
".fs", // FilterScript files
".rsh", // RenderScript header files
".d", // Dependency files
".java", // Java source files
".scala", // Scala source files
".class", // Java class files
".scc", // Visual SourceSafe
".swp", // vi swap file
".gwt.xml", // Google Web Toolkit modules
"~", // backup files
"/", // empty directory entries
".autodepsmetadata.pb", // auto deps metadata
}
excludedFilenames = []string{
"thumbs.db", // image index file
"picasa.ini", // image index file
"package.html", // Javadoc
"overview.html", // Javadoc
"protobuf.meta", // protocol buffer metadata
"flags.xml", // Google flags metadata
}
excludedDirectories = []string{
"cvs", // CVS repository files
".svn", // SVN repository files
"sccs", // SourceSafe repository files
"meta-inf", // jar metadata
}
)
func desc() string {
return "Extracts resources from a jar and put them into a separate zip file."
}
// Init initializes extractresources action.
func Init() {
// Empty placeholder function. Will get segfaults in ak if this doesn't exist.
}
// shouldExtractFile checks if the provided path describes a resource, and should be extracted.
func shouldExtractFile(path string) bool {
path = strings.ToLower(path)
for _, ext := range excludedExtensions {
if strings.HasSuffix(path, ext) {
return false
}
}
segments := strings.Split(path, "/")
filename := segments[len(segments)-1]
if strings.HasPrefix(filename, ".") || slices.Contains(excludedFilenames, filename) {
return false
}
dirs := segments[:len(segments)-1]
// allow META-INF/services at the root to support ServiceLoader
if len(dirs) >= 2 && dirs[0] == "meta-inf" && dirs[1] == "services" {
return true
}
// Check that no parts of the parent directory path should be excluded.
for _, dir := range excludedDirectories {
if slices.Contains(dirs, dir) {
return false
}
}
return true
}
func extractResources(inputJarFilename string, outputZipFilename string) error {
// Input jar reader setup
inputJar, err := zip.OpenReader(inputJarFilename)
if err != nil {
return err
}
defer inputJar.Close()
// Output zip writer setup
outputZipFile, err := os.Create(outputZipFilename)
if err != nil {
return err
}
defer outputZipFile.Close()
outputZipWriter := zip.NewWriter(outputZipFile)
defer outputZipWriter.Close()
for _, fileInZip := range inputJar.File {
if shouldExtractFile(fileInZip.Name) {
// Write the file to the output zip file.
fileHandle, err := outputZipWriter.CreateHeader(&zip.FileHeader{
Name: fileInZip.Name,
Method: zip.Store, // Don't use any compression, since the legacy tool did not.
})
if err != nil {
return err
}
fileInZipHandle, err := fileInZip.Open()
if err != nil {
return err
}
_, err = io.Copy(fileHandle, fileInZipHandle)
if err != nil {
return err
}
}
}
return nil
}
// Run is the main entry point for the extractresources binary.
func Run() {
// Args will be in the form of `ak extractresources input_jar output_zip`
if len(os.Args) != 4 {
log.Fatal("Usage: ak extractresources input_jar output_zip")
}
inputJarFilename := os.Args[2]
outputZipFilename := os.Args[3]
if err := extractResources(inputJarFilename, outputZipFilename); err != nil {
log.Fatal(err)
}
}