From a63088bdd23b2ed7163b86df1d0ae585cc4eabea Mon Sep 17 00:00:00 2001 From: Gabriel Konat Date: Fri, 26 Jul 2019 14:06:54 +0200 Subject: [PATCH] Unobserve auto-transform tasks when resource is deleted. Fix TigerParse throwing when input file does not exist. Fix TigerCompileDirectory's directory dependency being too broad. Add Iterator/Iterable utilities. Add license/copyright statements. --- LICENSE | 202 ++++++++++++++++++ NOTICE | 6 + .../java/mb/common/message/KeyedMessages.java | 5 +- .../main/java/mb/common/message/Message.java | 8 + .../main/java/mb/common/message/Messages.java | 28 ++- .../mb/common/message/MessagesBuilder.java | 8 +- .../main/java/mb/common/util/ArrayUtil.java | 2 + .../util/ClassLoaderObjectInputStream.java | 2 + .../src/main/java/mb/common/util/IntUtil.java | 1 + .../java/mb/common/util/IterableUtil.java | 35 +++ .../java/mb/common/util/IteratorUtil.java | 37 ++++ .../main/java/mb/common/util/ListView.java | 4 + .../java/mb/common/util/MultiHashMap.java | 11 +- .../java/mb/common/util/Preconditions.java | 2 + .../common/util/SerializationException.java | 2 + .../mb/common/util/SerializationUtil.java | 2 + .../java/mb/common/util/StringFormatter.java | 2 +- .../main/java/mb/common/util/StringUtil.java | 2 + .../eclipse/build/SpoofaxProjectBuilder.java | 2 +- .../mb/spoofax/eclipse/pie/PieRunner.java | 124 +++++++---- .../mb/tiger/spoofax/taskdef/TigerParse.java | 17 +- .../transform/TigerCompileDirectory.java | 6 +- 22 files changed, 446 insertions(+), 62 deletions(-) create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 common/src/main/java/mb/common/util/IterableUtil.java create mode 100644 common/src/main/java/mb/common/util/IteratorUtil.java diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..ddceda61f --- /dev/null +++ b/NOTICE @@ -0,0 +1,6 @@ +Spoofax-PIE +Copyright (C) 2019 Delft University of Technology + +This project includes software developed at the Programming Languages Group at Delft University of Technology (http://www.tudelft.nl). + +This product includes software developed at The Apache Software Foundation (http://www.apache.org/), copyright (C) 2001-2019 The Apache Software Foundation \ No newline at end of file diff --git a/common/src/main/java/mb/common/message/KeyedMessages.java b/common/src/main/java/mb/common/message/KeyedMessages.java index b3eaaf629..905bd6e98 100644 --- a/common/src/main/java/mb/common/message/KeyedMessages.java +++ b/common/src/main/java/mb/common/message/KeyedMessages.java @@ -1,5 +1,6 @@ package mb.common.message; +import mb.common.util.ListView; import mb.common.util.MultiHashMap; import mb.resource.ResourceKey; import org.checkerframework.checker.nullness.qual.Nullable; @@ -112,8 +113,8 @@ public boolean containsTraceOrHigher() { public Messages asMessages() { - return new Messages( - messages.values().stream().flatMap(Collection::stream).collect(Collectors.toCollection(ArrayList::new))); + final ArrayList list = messages.values().stream().flatMap(Collection::stream).collect(Collectors.toCollection(ArrayList::new)); + return new Messages(new ListView<>(list)); } diff --git a/common/src/main/java/mb/common/message/Message.java b/common/src/main/java/mb/common/message/Message.java index 6de0300af..58dcfcf8e 100644 --- a/common/src/main/java/mb/common/message/Message.java +++ b/common/src/main/java/mb/common/message/Message.java @@ -26,10 +26,18 @@ public Message(String text, Severity severity, @Nullable Region region) { this(text, null, severity, region); } + public Message(String text, Throwable exception, Severity severity) { + this(text, exception, severity, null); + } + public Message(String text, Severity severity) { this(text, null, severity, null); } + public Message(String text, Throwable exception) { + this(text, exception, Severity.Error, null); + } + @Override public boolean equals(Object o) { if(this == o) return true; diff --git a/common/src/main/java/mb/common/message/Messages.java b/common/src/main/java/mb/common/message/Messages.java index 92e2ebfbc..176a689f3 100644 --- a/common/src/main/java/mb/common/message/Messages.java +++ b/common/src/main/java/mb/common/message/Messages.java @@ -1,16 +1,40 @@ package mb.common.message; +import mb.common.util.ListView; + import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; public class Messages implements Serializable { - final ArrayList messages; + final ListView messages; - public Messages(ArrayList messages) { + public Messages(ListView messages) { this.messages = messages; } + public static Messages of() { + return new Messages(ListView.of()); + } + + public static Messages of(Message message) { + final ArrayList list = new ArrayList<>(); + list.add(message); + return new Messages(new ListView<>(list)); + } + + public static Messages of(Message... messages) { + final ArrayList list = new ArrayList<>(); + Collections.addAll(list, messages); + return new Messages(new ListView<>(list)); + } + + public static Messages copyOf(List messages) { + return new Messages(new ListView<>(new ArrayList<>(messages))); + } + public int size() { return messages.size(); diff --git a/common/src/main/java/mb/common/message/MessagesBuilder.java b/common/src/main/java/mb/common/message/MessagesBuilder.java index 48c8ff253..4388fd732 100644 --- a/common/src/main/java/mb/common/message/MessagesBuilder.java +++ b/common/src/main/java/mb/common/message/MessagesBuilder.java @@ -1,6 +1,8 @@ package mb.common.message; import mb.common.region.Region; +import mb.common.util.IterableUtil; +import mb.common.util.ListView; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; @@ -36,7 +38,7 @@ public void addMessages(Collection messages) { } public void addMessages(Messages messages) { - this.messages.addAll(messages.messages); + IterableUtil.addAll(this.messages, messages.messages); } @@ -47,7 +49,7 @@ public void replaceMessages(Collection messages) { public void replaceMessages(Messages messages) { this.messages.clear(); - this.messages.addAll(messages.messages); + IterableUtil.addAll(this.messages, messages.messages); } @@ -57,6 +59,6 @@ public void clearAll() { public Messages build() { - return new Messages(new ArrayList<>(this.messages)); + return new Messages(ListView.copyOf(messages)); } } diff --git a/common/src/main/java/mb/common/util/ArrayUtil.java b/common/src/main/java/mb/common/util/ArrayUtil.java index fc499b862..8060f9060 100644 --- a/common/src/main/java/mb/common/util/ArrayUtil.java +++ b/common/src/main/java/mb/common/util/ArrayUtil.java @@ -1,4 +1,6 @@ /* + * Modifications copyright (C) 2019 Delft University of Technology + * * 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. diff --git a/common/src/main/java/mb/common/util/ClassLoaderObjectInputStream.java b/common/src/main/java/mb/common/util/ClassLoaderObjectInputStream.java index b88d26d48..a0e544687 100644 --- a/common/src/main/java/mb/common/util/ClassLoaderObjectInputStream.java +++ b/common/src/main/java/mb/common/util/ClassLoaderObjectInputStream.java @@ -1,4 +1,6 @@ /* + * Modifications copyright (C) 2019 Delft University of Technology + * * 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. diff --git a/common/src/main/java/mb/common/util/IntUtil.java b/common/src/main/java/mb/common/util/IntUtil.java index a4a6b56c5..c28adf107 100644 --- a/common/src/main/java/mb/common/util/IntUtil.java +++ b/common/src/main/java/mb/common/util/IntUtil.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 The Guava Authors + * Modifications copyright (C) 2019 Delft University of Technology * * 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 diff --git a/common/src/main/java/mb/common/util/IterableUtil.java b/common/src/main/java/mb/common/util/IterableUtil.java new file mode 100644 index 000000000..4ca49659d --- /dev/null +++ b/common/src/main/java/mb/common/util/IterableUtil.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2007 The Guava Authors + * Modifications copyright (C) 2019 Delft University of Technology + * + * 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 mb.common.util; + +import java.util.Collection; + +// Selectively copied from https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/Iterables.java +public class IterableUtil { + /** + * Adds all elements in {@code iterable} to {@code collection}. + * + * @return {@code true} if {@code collection} was modified as a result of this operation. + */ + public static boolean addAll(Collection addTo, Iterable elementsToAdd) { + if(elementsToAdd instanceof Collection) { + @SuppressWarnings("unchecked") final Collection c = (Collection) elementsToAdd; + return addTo.addAll(c); + } + return IteratorUtil.addAll(addTo, elementsToAdd.iterator()); + } +} diff --git a/common/src/main/java/mb/common/util/IteratorUtil.java b/common/src/main/java/mb/common/util/IteratorUtil.java new file mode 100644 index 000000000..9792594b0 --- /dev/null +++ b/common/src/main/java/mb/common/util/IteratorUtil.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2007 The Guava Authors + * Modifications copyright (C) 2019 Delft University of Technology + * + * 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 mb.common.util; + +import java.util.Collection; +import java.util.Iterator; + +// Selectively copied from https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/Iterators.java +public class IteratorUtil { + /** + * Adds all elements in {@code iterator} to {@code collection}. The iterator will be left exhausted: its {@code + * hasNext()} method will return {@code false}. + * + * @return {@code true} if {@code collection} was modified as a result of this operation + */ + public static boolean addAll(Collection addTo, Iterator iterator) { + boolean wasModified = false; + while(iterator.hasNext()) { + wasModified |= addTo.add(iterator.next()); + } + return wasModified; + } +} diff --git a/common/src/main/java/mb/common/util/ListView.java b/common/src/main/java/mb/common/util/ListView.java index 90095801d..f4bcb944e 100644 --- a/common/src/main/java/mb/common/util/ListView.java +++ b/common/src/main/java/mb/common/util/ListView.java @@ -33,6 +33,10 @@ public static ListView of(E element) { return new ListView<>(list); } + public static ListView copyOf(List list) { + return new ListView<>(new ArrayList<>(list)); + } + public E get(int index) { return collection.get(index); diff --git a/common/src/main/java/mb/common/util/MultiHashMap.java b/common/src/main/java/mb/common/util/MultiHashMap.java index 48f428767..c100f47f3 100644 --- a/common/src/main/java/mb/common/util/MultiHashMap.java +++ b/common/src/main/java/mb/common/util/MultiHashMap.java @@ -3,12 +3,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Set; import java.util.function.BiConsumer; public class MultiHashMap implements Serializable { @@ -52,6 +48,11 @@ public void putAll(@Nullable K key, Collection values) { map.computeIfAbsent(key, k -> new ArrayList<>()).addAll(values); } + public void putAll(@Nullable K key, Iterable values) { + final ArrayList list = map.computeIfAbsent(key, k -> new ArrayList<>()); + IterableUtil.addAll(list, values); + } + public void putAll(Map> mapping) { for(Entry> entry : mapping.entrySet()) { final K key = entry.getKey(); diff --git a/common/src/main/java/mb/common/util/Preconditions.java b/common/src/main/java/mb/common/util/Preconditions.java index 4a01dd356..23d991017 100644 --- a/common/src/main/java/mb/common/util/Preconditions.java +++ b/common/src/main/java/mb/common/util/Preconditions.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Guava Authors + * Modifications copyright (C) 2019 Delft University of Technology * * 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 @@ -15,6 +16,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; +// Selectively copied from https://github.com/google/guava/blob/master/guava/src/com/google/common/base/Preconditions.java public class Preconditions { /** * Ensures the truth of an expression involving one or more parameters to the calling method. diff --git a/common/src/main/java/mb/common/util/SerializationException.java b/common/src/main/java/mb/common/util/SerializationException.java index 2746a4dba..927072449 100644 --- a/common/src/main/java/mb/common/util/SerializationException.java +++ b/common/src/main/java/mb/common/util/SerializationException.java @@ -1,4 +1,6 @@ /* + * Modifications copyright (C) 2019 Delft University of Technology + * * 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. diff --git a/common/src/main/java/mb/common/util/SerializationUtil.java b/common/src/main/java/mb/common/util/SerializationUtil.java index 37e7cc5b7..babb8a1b9 100644 --- a/common/src/main/java/mb/common/util/SerializationUtil.java +++ b/common/src/main/java/mb/common/util/SerializationUtil.java @@ -1,4 +1,6 @@ /* + * Modifications copyright (C) 2019 Delft University of Technology + * * 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. diff --git a/common/src/main/java/mb/common/util/StringFormatter.java b/common/src/main/java/mb/common/util/StringFormatter.java index 515212dcb..761030122 100644 --- a/common/src/main/java/mb/common/util/StringFormatter.java +++ b/common/src/main/java/mb/common/util/StringFormatter.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2004-2011 QOS.ch + * Modifications copyright (C) 2019 Delft University of Technology * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining @@ -20,7 +21,6 @@ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * */ package mb.common.util; diff --git a/common/src/main/java/mb/common/util/StringUtil.java b/common/src/main/java/mb/common/util/StringUtil.java index 9d5dc4b68..ad3a139ac 100644 --- a/common/src/main/java/mb/common/util/StringUtil.java +++ b/common/src/main/java/mb/common/util/StringUtil.java @@ -1,4 +1,6 @@ /* + * Modifications copyright (C) 2019 Delft University of Technology + * * 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. diff --git a/spoofax.eclipse/src/main/java/mb/spoofax/eclipse/build/SpoofaxProjectBuilder.java b/spoofax.eclipse/src/main/java/mb/spoofax/eclipse/build/SpoofaxProjectBuilder.java index 26c2d6d91..37193afbd 100644 --- a/spoofax.eclipse/src/main/java/mb/spoofax/eclipse/build/SpoofaxProjectBuilder.java +++ b/spoofax.eclipse/src/main/java/mb/spoofax/eclipse/build/SpoofaxProjectBuilder.java @@ -56,7 +56,7 @@ private void fullBuild(IProject eclipseProject, @Nullable IProgressMonitor monit pieRunner.fullBuild(languageComponent, eclipseProject, monitor); } - private void incrBuild(IProject eclipseProject, IResourceDelta delta, @Nullable IProgressMonitor monitor) throws CoreException, ExecException, InterruptedException { + private void incrBuild(IProject eclipseProject, IResourceDelta delta, @Nullable IProgressMonitor monitor) throws CoreException, ExecException, InterruptedException, IOException { pieRunner.incrementalBuild(languageComponent, eclipseProject, delta, monitor); } diff --git a/spoofax.eclipse/src/main/java/mb/spoofax/eclipse/pie/PieRunner.java b/spoofax.eclipse/src/main/java/mb/spoofax/eclipse/pie/PieRunner.java index f682f5002..ed5e2d4c3 100644 --- a/spoofax.eclipse/src/main/java/mb/spoofax/eclipse/pie/PieRunner.java +++ b/spoofax.eclipse/src/main/java/mb/spoofax/eclipse/pie/PieRunner.java @@ -126,7 +126,7 @@ public void fullBuild( try(final PieSession session = languageComponent.newPieSession()) { updateAffectedBy(resourceChanges.changed, session, monitor); - observeAutoTransforms(languageComponent, resourceChanges, session, monitor); + observeUnobserveAutoTransforms(languageComponent, resourceChanges, session, monitor); } } @@ -135,7 +135,7 @@ public void incrementalBuild( IProject project, IResourceDelta delta, @Nullable IProgressMonitor monitor - ) throws ExecException, InterruptedException, CoreException { + ) throws ExecException, InterruptedException, CoreException, IOException { logger.trace("Running incremental build for project '{}'", project); final ResourceChanges resourceChanges = new ResourceChanges(delta, languageComponent.getLanguageInstance().getFileExtensions()); @@ -143,7 +143,7 @@ public void incrementalBuild( bottomUpWorkspaceUpdate = workspaceUpdateFactory.create(languageComponent); try(final PieSession session = languageComponent.newPieSession()) { updateAffectedBy(resourceChanges.changed, session, monitor); - observeAutoTransforms(languageComponent, resourceChanges, session, monitor); + observeUnobserveAutoTransforms(languageComponent, resourceChanges, session, monitor); } bottomUpWorkspaceUpdate.update(null, monitor); bottomUpWorkspaceUpdate = null; @@ -176,8 +176,7 @@ public void clean( unobserve(def.createTask(new TransformInput(TransformSubjects.file(file))), pie, session); } } - // Delete unobserved tasks (garbage collection). - session.deleteUnobservedTasks((t) -> true, (t, r) -> true); + deleteUnobservedTasks(session); } } @@ -190,7 +189,7 @@ public void startup( ) throws IOException, CoreException, ExecException, InterruptedException { final ResourceChanges resourceChanges = new ResourceChanges(languageComponent.getEclipseIdentifiers().getNature(), languageComponent.getLanguageInstance().getFileExtensions()); try(final PieSession session = languageComponent.newPieSession()) { - observeAutoTransforms(languageComponent, resourceChanges, session, monitor); + observeUnobserveAutoTransforms(languageComponent, resourceChanges, session, monitor); } } @@ -283,6 +282,11 @@ public void unobserve(Task task, Pie pie, PieSession session) { session.unobserve(key); } + public void deleteUnobservedTasks(PieSession session) throws IOException { + logger.trace("Deleting unobserved tasks"); + session.deleteUnobservedTasks((t) -> true, (t, r) -> true); + } + // Helper/utility classes and methods. @@ -296,43 +300,65 @@ private static Cancelled monitorCancelled(@Nullable IProgressMonitor monitor) { private static class ResourceChanges { - public final HashSet changed = new HashSet<>(); - public final ArrayList newProjects = new ArrayList<>(); - public final ArrayList newDirectories = new ArrayList<>(); - public final ArrayList newFiles = new ArrayList<>(); + final HashSet changed = new HashSet<>(); + final ArrayList newProjects = new ArrayList<>(); + final ArrayList newDirectories = new ArrayList<>(); + final ArrayList newFiles = new ArrayList<>(); + final ArrayList removedProjects = new ArrayList<>(); + final ArrayList removedDirectories = new ArrayList<>(); + final ArrayList removedFiles = new ArrayList<>(); + + boolean hasRemovedResources() { + return !(removedProjects.isEmpty() && removedDirectories.isEmpty() && removedFiles.isEmpty()); + } - public ResourceChanges(EclipseResource project, SetView extensions) throws IOException { + ResourceChanges(EclipseResource project, SetView extensions) throws IOException { walkProject(project, extensions); } - public ResourceChanges(IResourceDelta delta, SetView extensions) throws CoreException { + ResourceChanges(IResourceDelta delta, SetView extensions) throws CoreException { delta.accept((d) -> { + final int kind = d.getKind(); + final boolean added = kind == IResourceDelta.ADDED; + final boolean removed = kind == IResourceDelta.REMOVED; final IResource resource = d.getResource(); final EclipseResourcePath path = new EclipseResourcePath(resource); - changed.add(path); // Also mark non-language resources as changed, since tasks may require/provide non-language resources. - if(d.getKind() == IResourceDelta.ADDED) { - switch(resource.getType()) { - case IResource.PROJECT: + // Do not mark removed resources as changed, as tasks for removed resources should be unobserved instead of being executed. + if(!removed) { + // Mark all resources as changed, since tasks may require/provide non-language resources. + changed.add(path); + } + switch(resource.getType()) { + case IResource.PROJECT: + if(added) { newProjects.add(path); - break; - case IResource.FOLDER: + } else if(removed) { + removedProjects.add(path); + } + break; + case IResource.FOLDER: + if(added) { newDirectories.add(path); - break; - case IResource.FILE: - final @Nullable String extension = path.getLeafExtension(); - if(extension != null) { - if(extensions.contains(extension)) { - newFiles.add(path); - } + } else if(removed) { + removedDirectories.add(path); + } + break; + case IResource.FILE: + final @Nullable String extension = path.getLeafExtension(); + if(extension != null && extensions.contains(extension)) { + if(added) { + newFiles.add(path); + } else if(removed) { + removedFiles.add(path); } - break; - } + } + break; } return true; }); } - public ResourceChanges(String projectNatureId, SetView extensions) throws IOException, CoreException { + ResourceChanges(String projectNatureId, SetView extensions) throws IOException, CoreException { for(IProject eclipseProject : ResourcesPlugin.getWorkspace().getRoot().getProjects()) { if(!eclipseProject.hasNature(projectNatureId)) continue; final EclipseResource project = new EclipseResource(eclipseProject); @@ -366,11 +392,11 @@ private void walkProject(EclipseResource project, SetView extensions) th private static class AutoTransformDefs { - public final CollectionView project; - public final CollectionView directory; - public final CollectionView file; + final CollectionView project; + final CollectionView directory; + final CollectionView file; - public AutoTransformDefs(EclipseLanguageComponent languageComponent) { + AutoTransformDefs(EclipseLanguageComponent languageComponent) { final ArrayList project = new ArrayList<>(); final ArrayList directory = new ArrayList<>(); final ArrayList file = new ArrayList<>(); @@ -392,27 +418,39 @@ public AutoTransformDefs(EclipseLanguageComponent languageComponent) { } } - private void observeAutoTransforms( + private void observeUnobserveAutoTransforms( EclipseLanguageComponent languageComponent, ResourceChanges resourceChanges, PieSession session, @Nullable IProgressMonitor monitor - ) throws ExecException, InterruptedException { + ) throws ExecException, InterruptedException, IOException { final AutoTransformDefs autoTransformDefs = new AutoTransformDefs(languageComponent); // OPTO: calculate once per language component - for(ResourcePath project : resourceChanges.newProjects) { - for(TransformDef def : autoTransformDefs.project) { - require(def.createTask(new TransformInput(TransformSubjects.project(project))), session, monitor); + for(TransformDef def : autoTransformDefs.project) { + for(ResourcePath newProject : resourceChanges.newProjects) { + require(def.createTask(new TransformInput(TransformSubjects.project(newProject))), session, monitor); + } + for(ResourcePath removedProject : resourceChanges.removedProjects) { + unobserve(def.createTask(new TransformInput(TransformSubjects.project(removedProject))), pie, session); } } - for(ResourcePath directory : resourceChanges.newDirectories) { - for(TransformDef def : autoTransformDefs.directory) { - require(def.createTask(new TransformInput(TransformSubjects.directory(directory))), session, monitor); + for(TransformDef def : autoTransformDefs.directory) { + for(ResourcePath newDirectory : resourceChanges.newDirectories) { + require(def.createTask(new TransformInput(TransformSubjects.directory(newDirectory))), session, monitor); + } + for(ResourcePath removedDirectory : resourceChanges.removedDirectories) { + unobserve(def.createTask(new TransformInput(TransformSubjects.directory(removedDirectory))), pie, session); } } - for(ResourcePath file : resourceChanges.newFiles) { - for(TransformDef def : autoTransformDefs.file) { - require(def.createTask(new TransformInput(TransformSubjects.file(file))), session, monitor); + for(TransformDef def : autoTransformDefs.file) { + for(ResourcePath newFile : resourceChanges.newFiles) { + require(def.createTask(new TransformInput(TransformSubjects.file(newFile))), session, monitor); } + for(ResourcePath removedFile : resourceChanges.removedFiles) { + unobserve(def.createTask(new TransformInput(TransformSubjects.file(removedFile))), pie, session); + } + } + if(resourceChanges.hasRemovedResources()) { + deleteUnobservedTasks(session); } } } diff --git a/tiger.spoofax/src/main/java/mb/tiger/spoofax/taskdef/TigerParse.java b/tiger.spoofax/src/main/java/mb/tiger/spoofax/taskdef/TigerParse.java index 39d207c0b..e3c347ee2 100644 --- a/tiger.spoofax/src/main/java/mb/tiger/spoofax/taskdef/TigerParse.java +++ b/tiger.spoofax/src/main/java/mb/tiger/spoofax/taskdef/TigerParse.java @@ -1,6 +1,10 @@ package mb.tiger.spoofax.taskdef; +import mb.common.message.Message; +import mb.common.message.Messages; +import mb.common.message.Severity; import mb.jsglr1.common.JSGLR1ParseResult; +import mb.jsglr1.common.JSGLR1ParseResults; import mb.pie.api.ExecContext; import mb.pie.api.TaskDef; import mb.resource.ReadableResource; @@ -29,10 +33,17 @@ public TigerParse(ResourceService resourceService, TigerParseTable parseTable) { @Override public JSGLR1ParseResult exec(ExecContext context, ResourceKey key) throws IOException, InterruptedException { - final TigerParser parser = new TigerParser(parseTable); final ReadableResource resource = resourceService.getResource(key); context.require(resource); - final String text = resource.readString(StandardCharsets.UTF_8); - return parser.parse(text, "Module"); + if(!resource.exists()) { + return JSGLR1ParseResults.failed(Messages.of(new Message("Cannot parse file '" + key + "', it does not exist", Severity.Error))); + } + try { + final String text = resource.readString(StandardCharsets.UTF_8); + final TigerParser parser = new TigerParser(parseTable); + return parser.parse(text, "Module"); + } catch(IOException e) { + return JSGLR1ParseResults.failed(Messages.of(new Message("Cannot parse file '" + key + "', reading contents failed unexpectedly", e))); + } } } diff --git a/tiger.spoofax/src/main/java/mb/tiger/spoofax/taskdef/transform/TigerCompileDirectory.java b/tiger.spoofax/src/main/java/mb/tiger/spoofax/taskdef/transform/TigerCompileDirectory.java index ec02f36fa..37ac11d5b 100644 --- a/tiger.spoofax/src/main/java/mb/tiger/spoofax/taskdef/transform/TigerCompileDirectory.java +++ b/tiger.spoofax/src/main/java/mb/tiger/spoofax/taskdef/transform/TigerCompileDirectory.java @@ -13,6 +13,7 @@ import mb.resource.hierarchical.match.AllResourceMatcher; import mb.resource.hierarchical.match.FileResourceMatcher; import mb.resource.hierarchical.match.PathResourceMatcher; +import mb.resource.hierarchical.match.ResourceMatcher; import mb.resource.hierarchical.match.path.ExtensionsPathMatcher; import mb.spoofax.core.language.transform.*; import mb.tiger.spoofax.taskdef.TigerListDefNames; @@ -39,13 +40,14 @@ public class TigerCompileDirectory implements TaskDef new RuntimeException("Cannot compile, subject '" + subject + "' is not a directory subject")); - final HierarchicalResource directory = context.require(directoryPath, ResourceStampers.modifiedDir()); + final ResourceMatcher matcher = new AllResourceMatcher(new FileResourceMatcher(), new PathResourceMatcher(new ExtensionsPathMatcher("tig"))); + final HierarchicalResource directory = context.require(directoryPath, ResourceStampers.modifiedDir(matcher)); final StringBuffer sb = new StringBuffer(); sb.append("[\n "); try { final AtomicBoolean first = new AtomicBoolean(true); - directory.list(new AllResourceMatcher(new FileResourceMatcher(), new PathResourceMatcher(new ExtensionsPathMatcher("tig")))).forEach((f) -> { + directory.list(matcher).forEach((f) -> { try { if(!first.get()) { sb.append(", ");