From 4a4ffeb14fce2cd909e747c39aea1045d7c61c01 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sun, 5 Feb 2017 11:46:20 -0600 Subject: [PATCH 01/10] Log element for annotation StackOverflow If multiResolve causes a StackOverflow for org.elixir_lang.annotator.Callable.visitCall, when catch it and use errorreport logger to log the element. --- src/org/elixir_lang/annonator/Callable.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/org/elixir_lang/annonator/Callable.java b/src/org/elixir_lang/annonator/Callable.java index 14b18d5f4..d638e12c7 100644 --- a/src/org/elixir_lang/annonator/Callable.java +++ b/src/org/elixir_lang/annonator/Callable.java @@ -11,6 +11,7 @@ import com.intellij.psi.*; import com.intellij.util.containers.ContainerUtil; import org.elixir_lang.ElixirSyntaxHighlighter; +import org.elixir_lang.errorreport.Logger; import org.elixir_lang.psi.call.Call; import org.jetbrains.annotations.NotNull; @@ -66,7 +67,15 @@ private void visitCall(@NotNull final Call call) { if (reference instanceof PsiPolyVariantReference) { PsiPolyVariantReference polyVariantReference = (PsiPolyVariantReference) reference; - ResolveResult[] resolveResults = polyVariantReference.multiResolve(false); + ResolveResult[] resolveResults; + + try { + resolveResults = polyVariantReference.multiResolve(false); + } catch (StackOverflowError stackOverflowError) { + Logger.error(Callable.class, "StackOverflowError when annotating Call", call); + resolveResults = new ResolveResult[0]; + } + List validResolveResults = ContainerUtil.filter( resolveResults, new Condition() { From f5cf92724b549ef50f800865f2fecd5d43a55031 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sun, 5 Feb 2017 16:35:43 -0600 Subject: [PATCH 02/10] Include file path in errorreport excerpt --- src/org/elixir_lang/errorreport/Logger.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/org/elixir_lang/errorreport/Logger.java b/src/org/elixir_lang/errorreport/Logger.java index 989cdef8b..5e0357f3f 100644 --- a/src/org/elixir_lang/errorreport/Logger.java +++ b/src/org/elixir_lang/errorreport/Logger.java @@ -112,6 +112,14 @@ private static String excerpt(@NotNull PsiFile containingFile, @NotNull PsiEleme excerptBuilder.append(startingLine); excerptBuilder.append('-'); excerptBuilder.append(endingLine); + + VirtualFile virtualFile = containingFile.getVirtualFile(); + + if (virtualFile != null) { + excerptBuilder.append(" in "); + excerptBuilder.append(virtualFile.getPath()); + } + excerptBuilder.append("\n"); } From 17ca0e693c4a16c0d8b53ea691fdcbb06c1fcccb Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sun, 5 Feb 2017 18:29:04 -0600 Subject: [PATCH 03/10] Log element for StackOverflow related to imports --- src/org/elixir_lang/psi/Import.java | 9 +++++++- .../psi/scope/CallDefinitionClause.java | 21 ++++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/org/elixir_lang/psi/Import.java b/src/org/elixir_lang/psi/Import.java index 8359a8b71..f8072aaac 100644 --- a/src/org/elixir_lang/psi/Import.java +++ b/src/org/elixir_lang/psi/Import.java @@ -60,7 +60,14 @@ public Boolean fun(@NotNull @SuppressWarnings("unused") Call call) { */ public static void callDefinitionClauseCallWhile(@NotNull Call importCall, @NotNull final Function function) { - Call modularCall = modular(importCall); + Call modularCall; + + try { + modularCall = modular(importCall); + } catch (StackOverflowError stackOverflowError) { + Logger.error(Import.class, "StackvoerflowError while finding modular for import", importCall); + modularCall = null; + } if (modularCall != null) { final Function optionsFilter = callDefinitionClauseCallFilter(importCall); diff --git a/src/org/elixir_lang/psi/scope/CallDefinitionClause.java b/src/org/elixir_lang/psi/scope/CallDefinitionClause.java index 6880a390f..06df86d99 100644 --- a/src/org/elixir_lang/psi/scope/CallDefinitionClause.java +++ b/src/org/elixir_lang/psi/scope/CallDefinitionClause.java @@ -5,6 +5,7 @@ import com.intellij.psi.ResolveState; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.util.Function; +import org.elixir_lang.errorreport.Logger; import org.elixir_lang.psi.Import; import org.elixir_lang.psi.call.Call; import org.elixir_lang.structure_view.element.modular.Module; @@ -80,15 +81,19 @@ protected boolean execute(@NotNull Call element, @NotNull final ResolveState sta } else if (Import.is(element)) { final ResolveState importState = state.put(IMPORT_CALL, element); - Import.callDefinitionClauseCallWhile( - element, - new Function() { - @Override - public Boolean fun(Call callDefinitionClause) { - return executeOnCallDefinitionClause(callDefinitionClause, importState); + try { + Import.callDefinitionClauseCallWhile( + element, + new Function() { + @Override + public Boolean fun(Call callDefinitionClause) { + return executeOnCallDefinitionClause(callDefinitionClause, importState); + } } - } - ); + ); + } catch (StackOverflowError stackOverflowError) { + Logger.error(CallDefinitionClause.class, "StackOverflowError while processing import", element); + } } else if (Module.is(element)) { Call[] childCalls = macroChildCalls(element); From 2b24574b482b188810343968a143bdc48d0ebb28 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sun, 5 Feb 2017 19:51:35 -0600 Subject: [PATCH 04/10] Skip import Kernel in kernel.ex Prevents stack overflow due to recursive import. --- .../psi/impl/ElixirPsiImplUtil.java | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/org/elixir_lang/psi/impl/ElixirPsiImplUtil.java b/src/org/elixir_lang/psi/impl/ElixirPsiImplUtil.java index 15a0c0932..62c12dc4f 100644 --- a/src/org/elixir_lang/psi/impl/ElixirPsiImplUtil.java +++ b/src/org/elixir_lang/psi/impl/ElixirPsiImplUtil.java @@ -4907,6 +4907,25 @@ public static OtpErlangObject quotedVariable(@NotNull final OtpErlangObject quot ); } + @Contract(pure = true) + @NotNull + public static boolean recursiveKernelImport(@NotNull QualifiableAlias qualifiableAlias, + @NotNull PsiElement maxScope) { + boolean recursiveKernelImport = false; + + if (maxScope instanceof ElixirFile) { + ElixirFile elixirFile = (ElixirFile) maxScope; + + if (elixirFile.getName().equals("kernel.ex")) { + String qualifiableAliasName = qualifiableAlias.getName(); + + recursiveKernelImport = qualifiableAliasName != null && qualifiableAliasName.equals(KERNEL); + } + } + + return recursiveKernelImport; + } + @Contract(pure = true) @NotNull public static int resolvedFinalArity(@NotNull final Call call) { @@ -5631,10 +5650,13 @@ public static Call maybeAliasToModular(@NotNull final PsiElement maybeAlias, @No if (maybeQualifiableAlias instanceof QualifiableAlias) { QualifiableAlias qualifiableAlias = (QualifiableAlias) maybeQualifiableAlias; - /* need to construct reference directly as qualified aliases don't return a - reference except for the outermost */ - PsiPolyVariantReference reference = new org.elixir_lang.reference.Module(qualifiableAlias, maxScope); - modular = aliasToModular(qualifiableAlias, reference); + + if (!recursiveKernelImport(qualifiableAlias, maxScope)) { + /* need to construct reference directly as qualified aliases don't return a reference except for the + outermost */ + PsiPolyVariantReference reference = new org.elixir_lang.reference.Module(qualifiableAlias, maxScope); + modular = aliasToModular(qualifiableAlias, reference); + } } return modular; From f533d9933b19b1961440fa24c5f00418567253d5 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sun, 5 Feb 2017 19:59:54 -0600 Subject: [PATCH 05/10] Failing regression test for #605 --- .../org/elixir_lang/annotator/module_attribute/issue_605.ex | 1 + tests/org/elixir_lang/annotator/ModuleAttributeTest.java | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 testData/org/elixir_lang/annotator/module_attribute/issue_605.ex diff --git a/testData/org/elixir_lang/annotator/module_attribute/issue_605.ex b/testData/org/elixir_lang/annotator/module_attribute/issue_605.ex new file mode 100644 index 000000000..5f83968d3 --- /dev/null +++ b/testData/org/elixir_lang/annotator/module_attribute/issue_605.ex @@ -0,0 +1 @@ +@spec (+value) :: value when value: number diff --git a/tests/org/elixir_lang/annotator/ModuleAttributeTest.java b/tests/org/elixir_lang/annotator/ModuleAttributeTest.java index b66c49ddf..f0596c2c3 100644 --- a/tests/org/elixir_lang/annotator/ModuleAttributeTest.java +++ b/tests/org/elixir_lang/annotator/ModuleAttributeTest.java @@ -39,6 +39,11 @@ public void testIssue525() { myFixture.checkHighlighting(false, false, true); } + public void testIssue605() { + myFixture.configureByFile("issue_605.ex"); + myFixture.checkHighlighting(false, false, true); + } + public void testMatch() { myFixture.configureByFile("match.ex"); myFixture.checkHighlighting(false, false, true); From 6fda81baa056390dc2e26adf74aa7706dcb07c44 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sun, 5 Feb 2017 20:14:45 -0600 Subject: [PATCH 06/10] Strip all outer parentheses from left type operand Fixes #605 --- src/org/elixir_lang/annonator/ModuleAttribute.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/org/elixir_lang/annonator/ModuleAttribute.java b/src/org/elixir_lang/annonator/ModuleAttribute.java index ad3dde9d6..dd1eb608f 100644 --- a/src/org/elixir_lang/annonator/ModuleAttribute.java +++ b/src/org/elixir_lang/annonator/ModuleAttribute.java @@ -32,6 +32,7 @@ import static org.elixir_lang.psi.impl.ElixirPsiImplUtil.identifierName; import static org.elixir_lang.psi.impl.ElixirPsiImplUtil.stripAccessExpression; import static org.elixir_lang.reference.ModuleAttribute.*; +import static org.elixir_lang.structure_view.element.CallDefinitionHead.stripAllOuterParentheses; /** * Annotates module attributes. @@ -578,9 +579,14 @@ private void highlightSpecification(AtUnqualifiedNoParenthesesCall atUnqualified if (leftOperand instanceof Type) { Type typeOperation = (Type) leftOperand; PsiElement typeOperationLeftOperand = typeOperation.leftOperand(); + PsiElement strippedTypeOperationLeftOperand = null; - if (typeOperationLeftOperand instanceof Call) { - Call call = (Call) typeOperationLeftOperand; + if (typeOperationLeftOperand != null) { + strippedTypeOperationLeftOperand = stripAllOuterParentheses(typeOperationLeftOperand); + } + + if (strippedTypeOperationLeftOperand instanceof Call) { + Call call = (Call) strippedTypeOperationLeftOperand; PsiElement functionNameElement = call.functionNameElement(); if (functionNameElement != null) { @@ -613,7 +619,7 @@ private void highlightSpecification(AtUnqualifiedNoParenthesesCall atUnqualified ); } } else { - cannotHighlightTypes(typeOperationLeftOperand); + cannotHighlightTypes(strippedTypeOperationLeftOperand); } PsiElement matchedTypeOperationRightOperand = typeOperation.rightOperand(); From d1e95088559bb953dfa724c32cac61d7ad620cec Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sun, 5 Feb 2017 20:55:44 -0600 Subject: [PATCH 07/10] Protect Module from IndexNotReadyException Fixes #606 Use advice from IndexNotReadyException documentation and check DumbService.isDumb(Project) before calling StubIndex.getElements. --- src/org/elixir_lang/reference/Module.java | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/org/elixir_lang/reference/Module.java b/src/org/elixir_lang/reference/Module.java index 32bc4045d..3fc3d25bf 100644 --- a/src/org/elixir_lang/reference/Module.java +++ b/src/org/elixir_lang/reference/Module.java @@ -1,6 +1,7 @@ package org.elixir_lang.reference; import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; @@ -16,6 +17,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import static org.elixir_lang.reference.module.ResolvableName.resolvableName; @@ -79,13 +81,19 @@ private List multiResolveProject(@NotNull Project project, @NotNull String name) { List results = new ArrayList(); - Collection namedElementCollection = StubIndex.getElements( - AllName.KEY, - name, - project, - GlobalSearchScope.allScope(project), - NamedElement.class - ); + Collection namedElementCollection; + + if (DumbService.isDumb(project)) { + namedElementCollection = Collections.emptyList(); + } else { + namedElementCollection = StubIndex.getElements( + AllName.KEY, + name, + project, + GlobalSearchScope.allScope(project), + NamedElement.class + ); + } for (NamedElement namedElement : namedElementCollection) { /* The NamedElement may be a ModuleImpl from a .beam. Using #getNaviationElement() ensures a source From 2bcd73a65c1f42e2084e048462f967f549cbff50 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Sun, 5 Feb 2017 21:16:56 -0600 Subject: [PATCH 08/10] Protect module.MultiResolve.indexedNameElements from IndexNotReadyException Fixes #606 Use advice from IndexNotReadyException documentation and check DumbService.isDumb(Project) before calling StubIndex.getElements. --- .../psi/scope/module/MultiResolve.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/org/elixir_lang/psi/scope/module/MultiResolve.java b/src/org/elixir_lang/psi/scope/module/MultiResolve.java index 12b8447cd..011a87023 100644 --- a/src/org/elixir_lang/psi/scope/module/MultiResolve.java +++ b/src/org/elixir_lang/psi/scope/module/MultiResolve.java @@ -1,5 +1,6 @@ package org.elixir_lang.psi.scope.module; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; @@ -15,6 +16,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import static org.elixir_lang.Module.concat; @@ -42,13 +44,21 @@ public static List resolveResultList(@NotNull String name, private static Collection indexedNamedElements(@NotNull PsiNamedElement match, @NotNull String unaliasedName) { Project project = match.getProject(); - return StubIndex.getElements( - AllName.KEY, - unaliasedName, - project, - GlobalSearchScope.allScope(project), - NamedElement.class - ); + Collection indexNamedElementCollection; + + if (DumbService.isDumb(project)) { + indexNamedElementCollection = Collections.emptyList(); + } else { + indexNamedElementCollection = StubIndex.getElements( + AllName.KEY, + unaliasedName, + project, + GlobalSearchScope.allScope(project), + NamedElement.class + ); + } + + return indexNamedElementCollection; } @Nullable From 8377ba3a30414e524530807228d3115e664a117b Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 7 Feb 2017 21:43:17 -0600 Subject: [PATCH 09/10] Better error messages for CallDefinitionClause.renderElement Fixes #563 Log what is Object when there is no PsiElement in LookupElement. --- .../CallDefinitionClause.java | 112 ++++++++++++++---- 1 file changed, 88 insertions(+), 24 deletions(-) diff --git a/src/org/elixir_lang/code_insight/lookup/element_renderer/CallDefinitionClause.java b/src/org/elixir_lang/code_insight/lookup/element_renderer/CallDefinitionClause.java index e06adff79..734d381f4 100644 --- a/src/org/elixir_lang/code_insight/lookup/element_renderer/CallDefinitionClause.java +++ b/src/org/elixir_lang/code_insight/lookup/element_renderer/CallDefinitionClause.java @@ -1,10 +1,13 @@ package org.elixir_lang.code_insight.lookup.element_renderer; +import com.google.common.base.Joiner; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementPresentation; +import com.intellij.diagnostic.LogMessageEx; import com.intellij.navigation.ItemPresentation; +import com.intellij.openapi.diagnostic.Attachment; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiElement; -import org.elixir_lang.errorreport.Logger; import org.elixir_lang.psi.call.Call; import org.jetbrains.annotations.NotNull; @@ -35,37 +38,98 @@ public void renderElement(LookupElement element, LookupElementPresentation prese PsiElement psiElement = element.getPsiElement(); - assert psiElement != null; - - if (psiElement instanceof Call) { - Call call = (Call) psiElement; + if (psiElement == null) { + renderObject(element); + } else { + renderPsiElement(psiElement, presentation); + } + } - if (org.elixir_lang.structure_view.element.CallDefinitionClause.is(call)) { - org.elixir_lang.structure_view.element.CallDefinitionClause structureView = - org.elixir_lang.structure_view.element.CallDefinitionClause.fromCall(call); + /* + * Private Instance Methods + */ - if (structureView != null) { - ItemPresentation structureViewPresentation = structureView.getPresentation(); + private void renderCall(@NotNull Call call, @NotNull LookupElementPresentation presentation) { + if (org.elixir_lang.structure_view.element.CallDefinitionClause.is(call)) { + renderCallDefinitionClause(call, presentation); + } + } - presentation.setIcon(structureViewPresentation.getIcon(true)); - String presentableText = structureViewPresentation.getPresentableText(); + private void renderCallDefinitionClause(@NotNull Call call, @NotNull LookupElementPresentation presentation) { + org.elixir_lang.structure_view.element.CallDefinitionClause structureView = + org.elixir_lang.structure_view.element.CallDefinitionClause.fromCall(call); - if (presentableText != null) { - int nameLength = name.length(); - int presentableTextLength = presentableText.length(); + if (structureView != null) { + renderStructureView(structureView, presentation); + } + } - if (nameLength <= presentableTextLength) { - presentation.appendTailText(presentableText.substring(nameLength), true); - } - } + private void renderItemPresentation(@NotNull ItemPresentation itemPresentation, + @NotNull LookupElementPresentation lookupElementPresentation) { + lookupElementPresentation.setIcon(itemPresentation.getIcon(true)); + String presentableText = itemPresentation.getPresentableText(); - String locationString = structureViewPresentation.getLocationString(); + if (presentableText != null) { + int nameLength = name.length(); + int presentableTextLength = presentableText.length(); - if (locationString != null) { - presentation.appendTailText(" (" + locationString + ")", false); - } - } + if (nameLength <= presentableTextLength) { + lookupElementPresentation.appendTailText(presentableText.substring(nameLength), true); } } + + String locationString = itemPresentation.getLocationString(); + + if (locationString != null) { + lookupElementPresentation.appendTailText(" (" + locationString + ")", false); + } + } + + private void renderObject(@NotNull LookupElement lookupElement) { + Logger logger = Logger.getInstance(CallDefinitionClause.class); + Object object = lookupElement.getObject(); + String userMessage = "CallDefinitionClause render called on LookupElement with null getPsiElement\n" + + "## name\n" + + "\n" + + "```\n" + + name + "\n" + + "```\n" + + "\n" + + "## getObject()\n"+ + "\n" + + "### toString()\n" + + "\n" + + "```\n" + + object.toString() + "\n" + + "```\n" + + "\n" + + "### getClass().getName()\n" + + "\n" + + "```\n" + + object.getClass().getName() + "\n" + + "```\n"; + + String details = Joiner.on("\n").join(new Throwable().getStackTrace()); + String title = "CallDefinitionClause render called on LookupElement with null getPsiElement"; + logger.error( + LogMessageEx.createEvent( + userMessage, + details, + title, + null, + (Attachment) null + ) + ); + } + + private void renderPsiElement(@NotNull PsiElement psiElement, @NotNull LookupElementPresentation presentation) { + if (psiElement instanceof Call) { + renderCall((Call) psiElement, presentation); + } + } + + private void renderStructureView(@NotNull org.elixir_lang.structure_view.element.CallDefinitionClause structureView, + @NotNull LookupElementPresentation presentation) { + renderItemPresentation(structureView.getPresentation(), presentation); } } From 730fc4e73c10b06f477f318c28d5c38a3fb516c8 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 7 Feb 2017 22:05:57 -0600 Subject: [PATCH 10/10] Update from ant 1.10.0 to 1.10.1 --- .../org_elixir_lang__ELIXIR_VERSION_1_1_1_.xml | 5 +---- .../org_elixir_lang__ELIXIR_VERSION_1_3_4_.xml | 5 +---- .../org_elixir_lang__ELIXIR_VERSION_1_4_0_.xml | 5 +---- .travis.yml | 6 +++--- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_1_1_.xml b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_1_1_.xml index bf0b6bc90..b7ff317ed 100644 --- a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_1_1_.xml +++ b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_1_1_.xml @@ -22,9 +22,6 @@ - - + \ No newline at end of file diff --git a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_3_4_.xml b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_3_4_.xml index 3a54aa52a..5336002e2 100644 --- a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_3_4_.xml +++ b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_3_4_.xml @@ -30,9 +30,6 @@ - - + \ No newline at end of file diff --git a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_4_0_.xml b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_4_0_.xml index 8f835574d..83723830f 100644 --- a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_4_0_.xml +++ b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_4_0_.xml @@ -22,9 +22,6 @@ - - + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index f44bfe5d7..a37608746 100644 --- a/.travis.yml +++ b/.travis.yml @@ -68,9 +68,9 @@ before_install: - tar xvfz jdk-8u112-linux-x64.tar.gz - export JAVA_HOME=${PWD}/jdk1.8.0_112 - export PATH=${JAVA_HOME}/bin:${PATH} -- wget http://www-us.apache.org/dist//ant/binaries/apache-ant-1.10.0-bin.tar.gz -- tar xvfz apache-ant-1.10.0-bin.tar.gz -- export PATH=${PWD}/apache-ant-1.10.0/bin:${PATH} +- wget http://www-us.apache.org/dist//ant/binaries/apache-ant-1.10.1-bin.tar.gz +- tar xvfz apache-ant-1.10.1-bin.tar.gz +- export PATH=${PWD}/apache-ant-1.10.1/bin:${PATH} - java -version - ant -version install: