Skip to content

Commit 7bdc4b8

Browse files
authored
Merge pull request #299 from warsaw/isfat-ontop
This is the isfat branch, rebased once more on top of the unrevert branch
2 parents b5bb338 + 748880d commit 7bdc4b8

File tree

11 files changed

+183
-68
lines changed

11 files changed

+183
-68
lines changed

docs/changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# pygradle news #
22

3+
* 2019-04-08
4+
- You must now change any usages of either the `python.pex.FatPex` or
5+
`python.pex.isFat` flag to `python.zipapp.isFat`.
6+
37
* 2018-11-09
48
- Added a default `mypy` task which you can enable by setting
59
`python.mypy.run = true` in your `build.gradle` file.

pygradle-plugin/src/integTest/groovy/com/linkedin/gradle/python/plugin/PexIntegrationTest.groovy

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,61 @@ class PexIntegrationTest extends Specification {
150150
then:
151151
out.toString() == "Hello World${System.getProperty("line.separator")}".toString()
152152
}
153+
154+
def "can build fat pex with isFat"() {
155+
given:
156+
testProjectDir.buildFile << """\
157+
| plugins {
158+
| id 'com.linkedin.python-pex'
159+
| }
160+
| version = '1.0.0'
161+
| python {
162+
| pex {
163+
| isFat = true
164+
| }
165+
| }
166+
| ${PyGradleTestBuilder.createRepoClosure()}
167+
""".stripMargin().stripIndent()
168+
169+
when:
170+
def result = GradleRunner.create()
171+
.withProjectDir(testProjectDir.root)
172+
.withArguments('build', '--stacktrace')
173+
.withPluginClasspath()
174+
.withDebug(true)
175+
.build()
176+
println result.output
177+
178+
then:
179+
180+
result.output.contains("BUILD SUCCESS")
181+
result.task(':foo:flake8').outcome == TaskOutcome.SUCCESS
182+
result.task(':foo:installPythonRequirements').outcome == TaskOutcome.SUCCESS
183+
result.task(':foo:installTestRequirements').outcome == TaskOutcome.SUCCESS
184+
result.task(':foo:createVirtualEnvironment').outcome == TaskOutcome.SUCCESS
185+
result.task(':foo:installProject').outcome == TaskOutcome.SUCCESS
186+
result.task(':foo:pytest').outcome == TaskOutcome.SUCCESS
187+
result.task(':foo:check').outcome == TaskOutcome.SUCCESS
188+
result.task(':foo:build').outcome == TaskOutcome.SUCCESS
189+
result.task(':foo:buildPex').outcome == TaskOutcome.SUCCESS
190+
result.task(':foo:assembleContainers').outcome == TaskOutcome.SUCCESS
191+
192+
Path deployablePath = testProjectDir.root.toPath().resolve(Paths.get('foo', 'build', 'deployable', "bin"))
193+
def pexFile = deployablePath.resolve(PexFileUtil.createFatPexFilename('hello_world'))
194+
195+
pexFile.toFile().exists()
196+
197+
when: "we have a pex file"
198+
def line = new String(pexFile.bytes, "UTF-8").substring(0, 100)
199+
200+
then: "its shebang line is not pointing to a virtualenv"
201+
line.startsWith("#!") && !line.contains("venv")
202+
203+
when:
204+
def out = ExecUtils.run(pexFile)
205+
println out
206+
207+
then:
208+
out.toString() == "Hello World${System.getProperty("line.separator")}".toString()
209+
}
153210
}

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

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import com.linkedin.gradle.python.tasks.BuildPexTask;
2020
import com.linkedin.gradle.python.util.ApplicationContainer;
2121
import com.linkedin.gradle.python.util.ExtensionUtils;
22-
import com.linkedin.gradle.python.util.OperatingSystem;
2322
import com.linkedin.gradle.python.util.StandardTextValues;
2423
import org.gradle.api.Project;
2524

@@ -39,12 +38,12 @@ public class PexExtension implements ApplicationContainer {
3938
public static final String TASK_BUILD_NOOP_PEX = "buildPex";
4039

4140
private File cache;
42-
// Default to fat zipapps on Windows, since our wrappers are fairly POSIX specific.
43-
private boolean isFat = OperatingSystem.current().isWindows();
4441
private boolean pythonWrapper = true;
42+
private Project project;
4543

4644
public PexExtension(Project project) {
4745
this.cache = new File(project.getBuildDir(), "pex-cache");
46+
this.project = project;
4847
}
4948

5049
public File getPexCache() {
@@ -55,40 +54,6 @@ public void setPexCache(File pexCache) {
5554
cache = pexCache;
5655
}
5756

58-
// These are kept for API backward compatibility.
59-
60-
/**
61-
* @return when <code>true</code>, then skinny pex's will be used.
62-
*/
63-
@Deprecated
64-
public boolean isFatPex() {
65-
return isFat();
66-
}
67-
68-
/**
69-
* @param fatPex when <code>true</code>, wrappers will be made all pointing to a single pex file.
70-
*/
71-
@Deprecated
72-
public void setFatPex(boolean fatPex) {
73-
isFat = fatPex;
74-
}
75-
76-
// Use these properties instead.
77-
78-
/**
79-
* @return when <code>true</code>, then skinny pex's will be used.
80-
*/
81-
public boolean isFat() {
82-
return isFat;
83-
}
84-
85-
/**
86-
* @param fat when <code>true</code>, wrappers will be made all pointing to a single pex file.
87-
*/
88-
public void setIsFat(boolean isFat) {
89-
this.isFat = isFat;
90-
}
91-
9257
/**
9358
* TODO: Revisit if this is needed.
9459
*
@@ -122,6 +87,27 @@ public void addDependencies(Project project) {
12287
}
12388

12489
public void makeTasks(Project project) {
125-
project.getTasks().create(TASK_BUILD_PEX, BuildPexTask.class);
90+
project.getTasks().maybeCreate(TASK_BUILD_PEX, BuildPexTask.class);
91+
}
92+
93+
// For backward compatibility in build.gradle flies.
94+
@Deprecated
95+
public boolean isFatPex() {
96+
return ExtensionUtils.getPythonComponentExtension(project, ZipappContainerExtension.class).isFat();
97+
}
98+
99+
@Deprecated
100+
public void setFatPex(boolean fatPex) {
101+
ExtensionUtils.getPythonComponentExtension(project, ZipappContainerExtension.class).setIsFat(fatPex);
102+
}
103+
104+
@Deprecated
105+
public boolean isFat() {
106+
return ExtensionUtils.getPythonComponentExtension(project, ZipappContainerExtension.class).isFat();
126107
}
108+
109+
@Deprecated
110+
public void setIsFat(boolean isFat) {
111+
ExtensionUtils.getPythonComponentExtension(project, ZipappContainerExtension.class).setIsFat(isFat);
112+
}
127113
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.extension;
17+
18+
import com.linkedin.gradle.python.util.OperatingSystem;
19+
20+
21+
/**
22+
* This class provides a stub in PythonExtension so that build.gradle files
23+
* can use the following construct:
24+
*
25+
* python {
26+
* zipapp.isFat = true
27+
* }
28+
*
29+
* This is the replacement for `python.pex.fatPex`, `python.pex.isFat`, and
30+
* `python.shiv.isFat`. The reason we need this is that we don't know which
31+
* container format the user wants until *after* build.gradle is evaluated,
32+
* but of course they want to set the isFat flag *in* their build.gradle.
33+
*/
34+
public class ZipappContainerExtension {
35+
private boolean isFat = OperatingSystem.current().isWindows();
36+
37+
public boolean isFat() {
38+
return isFat;
39+
}
40+
41+
public void setIsFat(boolean isFat) {
42+
this.isFat = isFat;
43+
}
44+
}

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

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
import com.linkedin.gradle.python.PythonExtension;
1919
import com.linkedin.gradle.python.extension.DeployableExtension;
2020
import com.linkedin.gradle.python.extension.PexExtension;
21+
import com.linkedin.gradle.python.extension.ZipappContainerExtension;
2122
import com.linkedin.gradle.python.tasks.BuildWheelsTask;
2223
import com.linkedin.gradle.python.tasks.NoopBuildPexTask;
24+
import com.linkedin.gradle.python.tasks.NoopTask;
2325
import com.linkedin.gradle.python.tasks.PythonContainerTask;
2426
import com.linkedin.gradle.python.util.ApplicationContainer;
2527
import com.linkedin.gradle.python.util.ExtensionUtils;
@@ -42,6 +44,8 @@ public void applyTo(final Project project) {
4244
final DeployableExtension deployableExtension = ExtensionUtils.maybeCreateDeployableExtension(project);
4345
final ApplicationContainer applicationContainer = pythonExtension.getApplicationContainer();
4446

47+
ExtensionUtils.maybeCreate(project, "zipapp", ZipappContainerExtension.class);
48+
4549
applicationContainer.addExtensions(project);
4650

4751
/*
@@ -53,7 +57,6 @@ public void applyTo(final Project project) {
5357
*/
5458
TaskContainer tasks = project.getTasks();
5559

56-
5760
// Add this no-op task for backward compatibility. See PexExtension for details.
5861
Task noop = tasks.findByName(PexExtension.TASK_BUILD_NOOP_PEX);
5962
if (noop == null) {
@@ -112,19 +115,21 @@ public void applyTo(final Project project) {
112115
postContainer.addDependencies(project);
113116
postContainer.makeTasks(project);
114117

115-
NoopBuildPexTask noopTask = (NoopBuildPexTask) tasks.findByName(PexExtension.TASK_BUILD_NOOP_PEX);
116-
noopTask.suppressWarning = true;
117-
118118
Task assemble = tasks.getByName(ApplicationContainer.TASK_ASSEMBLE_CONTAINERS);
119119
Task parent = tasks.getByName(ApplicationContainer.TASK_BUILD_PROJECT_WHEEL);
120120

121121
for (Task task : tasks.withType(PythonContainerTask.class)) {
122+
if (task instanceof NoopTask) {
123+
((NoopTask) task).setSuppressWarning(true);
124+
}
125+
122126
assemble.dependsOn(task);
123127
task.dependsOn(parent);
124-
}
125128

126-
// Turn the warning back on.
127-
noopTask.suppressWarning = false;
129+
if (task instanceof NoopTask) {
130+
((NoopTask) task).setSuppressWarning(false);
131+
}
132+
}
128133
});
129134
}
130135
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
*/
1616
package com.linkedin.gradle.python.plugin;
1717

18-
import com.linkedin.gradle.python.util.ApplicationContainer;
1918
import com.linkedin.gradle.python.extension.DeployableExtension;
19+
import com.linkedin.gradle.python.extension.PexExtension;
2020
import com.linkedin.gradle.python.tasks.BuildWebAppTask;
21+
import com.linkedin.gradle.python.util.ApplicationContainer;
2122
import com.linkedin.gradle.python.util.ExtensionUtils;
2223
import org.gradle.api.Project;
2324

@@ -33,11 +34,15 @@ public class PythonWebApplicationPlugin extends PythonBasePlugin {
3334

3435
@Override
3536
public void applyTo(final Project project) {
36-
3737
project.getPlugins().apply(PythonContainerPlugin.class);
3838

3939
final DeployableExtension deployableExtension = ExtensionUtils.maybeCreateDeployableExtension(project);
4040

41+
// 2019-04-11(warsaw): FIXME: For now, we're still hard coding pex
42+
// for the gunicorn file. Make sure the `pex` dependency is
43+
// installed.
44+
new PexExtension(project).addDependencies(project);
45+
4146
/*
4247
* Build a gunicorn pex file.
4348
*

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import com.linkedin.gradle.python.extension.DeployableExtension;
1919
import com.linkedin.gradle.python.extension.PexExtension;
20+
import com.linkedin.gradle.python.extension.ZipappContainerExtension;
2021
import com.linkedin.gradle.python.tasks.execution.FailureReasonProvider;
2122
import com.linkedin.gradle.python.tasks.execution.TeeOutputContainer;
2223
import com.linkedin.gradle.python.util.ExtensionUtils;
@@ -79,6 +80,8 @@ public void buildPex() throws Exception {
7980

8081
DeployableExtension deployableExtension = ExtensionUtils.getPythonComponentExtension(project, DeployableExtension.class);
8182
PexExtension pexExtension = ExtensionUtils.getPythonComponentExtension(project, PexExtension.class);
83+
ZipappContainerExtension zipappExtension = ExtensionUtils.getPythonComponentExtension(
84+
project, ZipappContainerExtension.class);
8285

8386
// Recreate the pex cache if it exists so that we don't mistakenly use an old build's version of the local project.
8487
if (pexExtension.getPexCache().exists()) {
@@ -88,7 +91,7 @@ public void buildPex() throws Exception {
8891

8992
deployableExtension.getDeployableBuildDir().mkdirs();
9093

91-
if (pexExtension.isFat()) {
94+
if (zipappExtension.isFat()) {
9295
new FatPexGenerator(project, pexOptions).buildEntryPoints();
9396
} else {
9497
new ThinPexGenerator(project, pexOptions, templateProvider, additionalProperties).buildEntryPoints();

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
package com.linkedin.gradle.python.tasks;
1717

1818
import com.linkedin.gradle.python.PythonExtension;
19-
import com.linkedin.gradle.python.extension.PexExtension;
19+
import com.linkedin.gradle.python.extension.ZipappContainerExtension;
2020
import com.linkedin.gradle.python.util.ExtensionUtils;
2121
import com.linkedin.gradle.python.util.PexFileUtil;
2222
import com.linkedin.gradle.python.util.entrypoint.EntryPointWriter;
@@ -68,10 +68,17 @@ public String getEntryPoint() {
6868
@TaskAction
6969
public void buildWebapp() throws IOException, ClassNotFoundException {
7070
Project project = getProject();
71-
PexExtension extension = ExtensionUtils.getPythonComponentExtension(project, PexExtension.class);
7271
PythonExtension pythonExtension = ExtensionUtils.getPythonExtension(project);
73-
74-
if (extension.isFat()) {
72+
ZipappContainerExtension zipappExtension = ExtensionUtils.getPythonComponentExtension(
73+
project, ZipappContainerExtension.class);
74+
75+
// Regardless of whether fat or thin zipapps are used, the container
76+
// plugin will build the right container (i.e. .pex or .pyz).
77+
// However, for thin zipapps, we need additional wrapper scripts
78+
// generated (e.g. the gunicorn wrapper).
79+
if (zipappExtension.isFat()) {
80+
// 2019-04-11(warsaw): FIXME: For now, we're still hard coding pex
81+
// for the gunicorn file.
7582
new FatPexGenerator(project, pexOptions).buildEntryPoint(
7683
PexFileUtil.createFatPexFilename(executable.getName()), entryPoint, null);
7784
} else {
@@ -104,5 +111,4 @@ public EntryPointTemplateProvider getTemplateProvider() {
104111
public void setTemplateProvider(EntryPointTemplateProvider templateProvider) {
105112
this.templateProvider = templateProvider;
106113
}
107-
108114
}

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import org.gradle.api.tasks.TaskAction;
2323

2424

25-
public class NoopBuildPexTask extends DefaultTask implements PythonContainerTask {
25+
public class NoopBuildPexTask extends DefaultTask implements PythonContainerTask, NoopTask {
2626
private static final Logger LOG = Logging.getLogger(NoopBuildPexTask.class);
2727
private static final String DISABLED_MESSAGE =
2828
"######################### WARNING ##########################\n"
@@ -32,7 +32,7 @@ public class NoopBuildPexTask extends DefaultTask implements PythonContainerTask
3232

3333
// This is used to suppress the warning when PythonContainerPlugin plumbs
3434
// this task into the task hierarchy, which isn't user code.
35-
public boolean suppressWarning = false;
35+
private boolean suppressWarning = false;
3636

3737
@TaskAction
3838
public void noOp() { }
@@ -43,4 +43,12 @@ public Task dependsOn(Object... paths) {
4343
}
4444
return super.dependsOn(paths);
4545
}
46+
47+
public boolean suppressWarning() {
48+
return suppressWarning;
49+
}
50+
51+
public void setSuppressWarning(boolean suppressWarning) {
52+
this.suppressWarning = suppressWarning;
53+
}
4654
}

0 commit comments

Comments
 (0)