Skip to content

Commit 5f446bd

Browse files
author
Ethan Hall
authored
Merge pull request #221 from warsaw/zipapp-refactor
Refactor to support generic Python "zipapps"
2 parents 24ea886 + 5a49834 commit 5f446bd

File tree

12 files changed

+197
-58
lines changed

12 files changed

+197
-58
lines changed

pygradle-plugin/src/main/groovy/com/linkedin/gradle/python/extension/PexExtension.java

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,56 @@
2121
import java.io.File;
2222

2323

24-
public class PexExtension {
25-
26-
private File pexCache;
27-
private boolean fatPex = OperatingSystem.current().isWindows(); //Defaulting to fat pex's on windows
24+
public class PexExtension implements ZipappExtension {
25+
private File cache;
26+
// Default to fat zipapps on Windows, since our wrappers are fairly POSIX specific.
27+
private boolean isFat = OperatingSystem.current().isWindows();
2828
private boolean pythonWrapper = true;
2929

3030
public PexExtension(Project project) {
31-
pexCache = new File(project.getBuildDir(), "pex-cache");
31+
this.cache = new File(project.getBuildDir(), "pex-cache");
3232
}
3333

3434
public File getPexCache() {
35-
return pexCache;
35+
return cache;
3636
}
3737

3838
public void setPexCache(File pexCache) {
39-
this.pexCache = pexCache;
39+
cache = pexCache;
4040
}
4141

42+
// These are kept for API backward compatibility.
43+
4244
/**
4345
* @return when <code>true</code>, then skinny pex's will be used.
4446
*/
47+
@Deprecated
4548
public boolean isFatPex() {
46-
return fatPex;
49+
return isFat();
4750
}
4851

4952
/**
5053
* @param fatPex when <code>true</code>, wrappers will be made all pointing to a single pex file.
5154
*/
55+
@Deprecated
5256
public void setFatPex(boolean fatPex) {
53-
this.fatPex = fatPex;
57+
isFat = fatPex;
58+
}
59+
60+
// Use these properties instead.
61+
62+
/**
63+
* @return when <code>true</code>, then skinny pex's will be used.
64+
*/
65+
public boolean isFat() {
66+
return isFat;
67+
}
68+
69+
/**
70+
* @param fat when <code>true</code>, wrappers will be made all pointing to a single pex file.
71+
*/
72+
public void setIsFat(boolean isFat) {
73+
this.isFat = isFat;
5474
}
5575

5676
/**
@@ -65,4 +85,12 @@ public boolean isPythonWrapper() {
6585
public void setPythonWrapper(boolean pythonWrapper) {
6686
this.pythonWrapper = pythonWrapper;
6787
}
88+
89+
public File getCache() {
90+
return cache;
91+
}
92+
93+
public void setCache(File cache) {
94+
this.cache = cache;
95+
}
6896
}

pygradle-plugin/src/main/groovy/com/linkedin/gradle/python/util/internal/pex/PexGenerator.java renamed to pygradle-plugin/src/main/groovy/com/linkedin/gradle/python/extension/ZipappExtension.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.linkedin.gradle.python.util.internal.pex;
16+
package com.linkedin.gradle.python.extension;
1717

18-
public interface PexGenerator {
1918

19+
public interface ZipappExtension {
2020
/**
21-
* When called will generate entry point files for a pex.
22-
*
23-
* @throws Exception when failing to build entry point
21+
* @return when <code>true</code>, then skinny pex's will be used.
2422
*/
25-
void buildEntryPoints() throws Exception;
23+
public boolean isFat();
24+
25+
/**
26+
* @param fat when <code>true</code>, wrappers will be made all pointing to a single pex file.
27+
*/
28+
public void setIsFat(boolean isFat);
2629
}

pygradle-plugin/src/main/groovy/com/linkedin/gradle/python/tasks/BuildPexTask.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
import com.linkedin.gradle.python.util.ExtensionUtils;
2323
import com.linkedin.gradle.python.util.internal.pex.FatPexGenerator;
2424
import com.linkedin.gradle.python.util.internal.pex.ThinPexGenerator;
25-
import com.linkedin.gradle.python.util.pex.DefaultEntryPointTemplateProvider;
26-
import com.linkedin.gradle.python.util.pex.EntryPointTemplateProvider;
25+
import com.linkedin.gradle.python.util.pex.DefaultPexEntryPointTemplateProvider;
26+
import com.linkedin.gradle.python.util.zipapp.EntryPointTemplateProvider;
2727
import org.apache.commons.io.FileUtils;
2828
import org.gradle.api.DefaultTask;
2929
import org.gradle.api.Project;
@@ -58,7 +58,7 @@
5858
public class BuildPexTask extends DefaultTask implements FailureReasonProvider {
5959

6060
private Map<String, String> additionalProperties;
61-
private EntryPointTemplateProvider templateProvider = new DefaultEntryPointTemplateProvider();
61+
private EntryPointTemplateProvider templateProvider = new DefaultPexEntryPointTemplateProvider();
6262
private TeeOutputContainer container = new TeeOutputContainer(System.out, System.err);
6363
private List<String> pexOptions = new ArrayList<>();
6464

@@ -87,7 +87,7 @@ public void buildPex() throws Exception {
8787

8888
deployableExtension.getDeployableBuildDir().mkdirs();
8989

90-
if (pexExtension.isFatPex()) {
90+
if (pexExtension.isFat()) {
9191
new FatPexGenerator(project, pexOptions).buildEntryPoints();
9292
} else {
9393
new ThinPexGenerator(project, pexOptions, templateProvider, additionalProperties).buildEntryPoints();

pygradle-plugin/src/main/groovy/com/linkedin/gradle/python/tasks/BuildWebAppTask.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public void buildWebapp() throws IOException, ClassNotFoundException {
6767
Project project = getProject();
6868
PexExtension pexExtension = ExtensionUtils.getPythonComponentExtension(project, PexExtension.class);
6969

70-
if (pexExtension.isFatPex()) {
70+
if (pexExtension.isFat()) {
7171
new FatPexGenerator(project, pexOptions).buildEntryPoint(
7272
PexFileUtil.createFatPexFilename(executable.getName()), entryPoint, null);
7373
} else {

pygradle-plugin/src/main/groovy/com/linkedin/gradle/python/util/internal/pex/FatPexGenerator.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,22 @@
1515
*/
1616
package com.linkedin.gradle.python.util.internal.pex;
1717

18+
import com.linkedin.gradle.python.PythonExtension;
1819
import com.linkedin.gradle.python.util.EntryPointHelpers;
1920
import com.linkedin.gradle.python.util.PexFileUtil;
21+
import com.linkedin.gradle.python.util.internal.zipapp.ZipappGenerator;
2022
import com.linkedin.gradle.python.util.pip.PipFreezeAction;
2123
import org.gradle.api.Project;
2224
import org.gradle.api.logging.Logger;
2325
import org.gradle.api.logging.Logging;
2426
import org.gradle.process.ExecResult;
2527

28+
import java.util.HashMap;
2629
import java.util.List;
2730
import java.util.Map;
2831

29-
public class FatPexGenerator implements PexGenerator {
32+
33+
public class FatPexGenerator implements ZipappGenerator {
3034

3135
private static final Logger logger = Logging.getLogger(FatPexGenerator.class);
3236

@@ -38,6 +42,12 @@ public FatPexGenerator(Project project, List<String> pexOptions) {
3842
this.pexOptions = pexOptions;
3943
}
4044

45+
@Override
46+
public Map<String, String> buildSubstitutions(PythonExtension extension, String entry) {
47+
// Not used for fat pexes.
48+
return new HashMap<>();
49+
}
50+
4151
@Override
4252
public void buildEntryPoints() {
4353
Map<String, String> dependencies = new PipFreezeAction(project).getDependencies();

pygradle-plugin/src/main/groovy/com/linkedin/gradle/python/util/internal/pex/ThinPexGenerator.java

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,53 +17,54 @@
1717

1818
import com.linkedin.gradle.python.PythonExtension;
1919
import com.linkedin.gradle.python.extension.DeployableExtension;
20+
import com.linkedin.gradle.python.extension.PexExtension;
2021
import com.linkedin.gradle.python.util.EntryPointHelpers;
2122
import com.linkedin.gradle.python.util.ExtensionUtils;
2223
import com.linkedin.gradle.python.util.PexFileUtil;
2324
import com.linkedin.gradle.python.util.entrypoint.EntryPointWriter;
24-
import com.linkedin.gradle.python.util.pex.EntryPointTemplateProvider;
25+
import com.linkedin.gradle.python.util.internal.zipapp.DefaultTemplateProviderOptions;
26+
import com.linkedin.gradle.python.util.internal.zipapp.ThinZipappGenerator;
2527
import com.linkedin.gradle.python.util.pip.PipFreezeAction;
28+
import com.linkedin.gradle.python.util.zipapp.EntryPointTemplateProvider;
2629
import org.gradle.api.Project;
27-
import org.gradle.api.logging.Logger;
2830
import org.gradle.api.logging.Logging;
2931
import org.gradle.process.ExecResult;
3032

3133
import java.io.File;
32-
import java.util.HashMap;
3334
import java.util.List;
3435
import java.util.Map;
3536

3637

37-
public class ThinPexGenerator implements PexGenerator {
38-
39-
private static final Logger logger = Logging.getLogger(ThinPexGenerator.class);
40-
41-
private final Project project;
42-
private final List<String> pexOptions;
43-
private final EntryPointTemplateProvider templateProvider;
44-
private final Map<String, String> extraProperties;
38+
public class ThinPexGenerator extends ThinZipappGenerator {
4539

4640
public ThinPexGenerator(
4741
Project project,
4842
List<String> pexOptions,
4943
EntryPointTemplateProvider templateProvider,
5044
Map<String, String> extraProperties) {
51-
this.project = project;
52-
this.pexOptions = pexOptions;
53-
this.templateProvider = templateProvider;
54-
this.extraProperties = extraProperties == null ? new HashMap<>() : extraProperties;
45+
46+
super(project, pexOptions, templateProvider, extraProperties);
47+
logger = Logging.getLogger(ThinPexGenerator.class);
48+
}
49+
50+
@Override
51+
public Map<String, String> buildSubstitutions(PythonExtension extension, String entry) {
52+
Map<String, String> substitutions = super.buildSubstitutions(extension, entry);
53+
substitutions.put("realPex", PexFileUtil.createThinPexFilename(project.getName()));
54+
return substitutions;
5555
}
5656

5757
@Override
5858
public void buildEntryPoints() throws Exception {
5959
PythonExtension extension = ExtensionUtils.getPythonExtension(project);
60+
PexExtension pexExtension = ExtensionUtils.getPythonComponentExtension(extension, PexExtension.class);
6061
DeployableExtension deployableExtension = ExtensionUtils.getPythonComponentExtension(
6162
extension, DeployableExtension.class);
6263

6364
Map<String, String> dependencies = new PipFreezeAction(project).getDependencies();
6465

6566
PexExecSpecAction action = PexExecSpecAction.withOutEntryPoint(
66-
project, project.getName(), pexOptions, dependencies);
67+
project, project.getName(), options, dependencies);
6768

6869
ExecResult exec = project.exec(action);
6970
new PexExecOutputParser(action, exec).validatePexBuildSuccessfully();
@@ -73,17 +74,13 @@ public void buildEntryPoints() throws Exception {
7374
String[] split = it.split("=");
7475
String name = split[0].trim();
7576
String entry = split[1].trim();
76-
77-
Map<String, String> propertyMap = new HashMap<>();
78-
propertyMap.putAll(extraProperties);
79-
propertyMap.put("realPex", PexFileUtil.createThinPexFilename(project.getName()));
80-
propertyMap.put("entryPoint", entry);
81-
propertyMap.put("pythonExecutable", extension.getDetails().getSystemPythonInterpreter().getAbsolutePath());
82-
propertyMap.put("toolName", project.getName());
77+
Map<String, String> substitutions = buildSubstitutions(extension, entry);
8378

8479
DefaultTemplateProviderOptions providerOptions = new DefaultTemplateProviderOptions(project, extension, entry);
85-
new EntryPointWriter(project, templateProvider.retrieveTemplate(providerOptions))
86-
.writeEntryPoint(new File(deployableExtension.getDeployableBinDir(), name), propertyMap);
80+
new EntryPointWriter(
81+
project,
82+
templateProvider.retrieveTemplate(providerOptions, pexExtension.isPythonWrapper()))
83+
.writeEntryPoint(new File(deployableExtension.getDeployableBinDir(), name), substitutions);
8784
}
8885
}
8986
}

pygradle-plugin/src/main/groovy/com/linkedin/gradle/python/util/internal/pex/DefaultTemplateProviderOptions.java renamed to pygradle-plugin/src/main/groovy/com/linkedin/gradle/python/util/internal/zipapp/DefaultTemplateProviderOptions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.linkedin.gradle.python.util.internal.pex;
16+
package com.linkedin.gradle.python.util.internal.zipapp;
1717

1818
import com.linkedin.gradle.python.PythonExtension;
19-
import com.linkedin.gradle.python.util.pex.TemplateProviderOptions;
19+
import com.linkedin.gradle.python.util.zipapp.TemplateProviderOptions;
2020
import org.gradle.api.Project;
2121

2222
/**
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2016 LinkedIn Corp.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.linkedin.gradle.python.util.internal.zipapp;
17+
18+
import com.linkedin.gradle.python.PythonExtension;
19+
import com.linkedin.gradle.python.util.zipapp.EntryPointTemplateProvider;
20+
import org.gradle.api.Project;
21+
import org.gradle.api.logging.Logger;
22+
import org.gradle.api.logging.Logging;
23+
24+
import java.util.HashMap;
25+
import java.util.List;
26+
import java.util.Map;
27+
28+
29+
public class ThinZipappGenerator implements ZipappGenerator {
30+
31+
protected static Logger logger = Logging.getLogger(ThinZipappGenerator.class);
32+
33+
protected final Project project;
34+
protected final List<String> options;
35+
protected final EntryPointTemplateProvider templateProvider;
36+
protected final Map<String, String> extraProperties;
37+
38+
public ThinZipappGenerator(
39+
Project project,
40+
List<String> options,
41+
EntryPointTemplateProvider templateProvider,
42+
Map<String, String> extraProperties) {
43+
44+
this.project = project;
45+
this.options = options;
46+
this.templateProvider = templateProvider;
47+
this.extraProperties = extraProperties == null ? new HashMap<>() : extraProperties;
48+
}
49+
50+
@Override
51+
public Map<String, String> buildSubstitutions(PythonExtension extension, String entry) {
52+
Map<String, String> substitutions = new HashMap<>();
53+
substitutions.putAll(extraProperties);
54+
substitutions.put("entryPoint", entry);
55+
substitutions.put("pythonExecutable", extension.getDetails().getSystemPythonInterpreter().getAbsolutePath());
56+
substitutions.put("toolName", project.getName());
57+
return substitutions;
58+
}
59+
60+
@Override
61+
public void buildEntryPoints() throws Exception {
62+
// Generic zipapps don't build anything, so subclasses should override
63+
// this to build their entry point specific artifacts.
64+
};
65+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2016 LinkedIn Corp.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.linkedin.gradle.python.util.internal.zipapp;
18+
19+
import com.linkedin.gradle.python.PythonExtension;
20+
import java.util.Map;
21+
22+
23+
public interface ZipappGenerator {
24+
25+
/**
26+
* When called, create the substitution map for the template generation.
27+
*/
28+
Map<String, String> buildSubstitutions(PythonExtension extension, String entry);
29+
30+
/**
31+
* When called will generate entry point files for a zipapp.
32+
*
33+
* @throws Exception when failing to build entry point
34+
*/
35+
void buildEntryPoints() throws Exception;
36+
}

0 commit comments

Comments
 (0)