##// END OF EJS Templates
WIP code cleanup after AI... never again!
cin -
r48:1f2da15fa080 default
parent child
Show More
@@ -0,0 +1,31
1 # design notes
2
3 ## core model
4
5 - OutgoingRegistry (Variant)
6 исходящая конфигурация
7 - [provider] configuration
8 - [container, live] slots
9 набор вариантов (слотов)
10 - [property] primarySlot
11 - AssemblyRegistry (Varaint, Slot)
12 содержимое слота может быть добавлено после появления слота в OutgoingRegistry
13 - assembleTask
14 - inputs
15 - compile unit output (CompileUnit, String)
16 - direct object (task, file collection, etc.)
17 - artifact: directory
18 - customTask
19 - artifact: FileSystemLocation
20
21 ## extension
22
23 - adapter
24 - whenFinalized
25 - dsl
26 - variant(name)
27 - slot(name)
28 - from***
29 - whenFinalized
30 - whenOutgoingConfiguration
31 - whenOutgoingSlot
@@ -0,0 +1,99
1 package org.implab.gradle.variants.artifacts;
2
3 import java.util.LinkedHashMap;
4 import java.util.Map;
5 import java.util.Optional;
6 import java.util.function.Function;
7
8 import org.eclipse.jdt.annotation.NonNullByDefault;
9 import org.gradle.api.InvalidUserDataException;
10 import org.gradle.api.Task;
11 import org.gradle.api.file.Directory;
12 import org.gradle.api.file.FileCollection;
13 import org.gradle.api.file.FileSystemLocation;
14 import org.gradle.api.model.ObjectFactory;
15 import org.gradle.api.provider.Provider;
16 import org.gradle.api.tasks.Copy;
17 import org.gradle.api.tasks.TaskContainer;
18 import org.gradle.api.tasks.TaskProvider;
19 import org.gradle.language.base.plugins.LifecycleBasePlugin;
20
21 @NonNullByDefault
22 public class ArtifactAssemblyRegistry implements ArtifactAssemblies {
23 private final ObjectFactory objects;
24 private final TaskContainer tasks;
25 private final Map<ArtifactSlot, ArtifactAssembly> assemblies = new LinkedHashMap<>();
26
27 public ArtifactAssemblyRegistry(ObjectFactory objects, TaskContainer tasks) {
28 this.objects = objects;
29 this.tasks = tasks;
30 }
31
32 public ArtifactAssembly register(
33 ArtifactSlot slot,
34 String taskName,
35 Provider<Directory> outputDirectory,
36 FileCollection sources) {
37
38 var task = tasks.register(taskName, Copy.class, copy -> {
39 copy.setGroup(LifecycleBasePlugin.BUILD_GROUP);
40 copy.into(outputDirectory);
41 copy.from(sources);
42 });
43
44 return register(slot, task, t -> outputDirectory);
45 }
46
47 public <T extends Task> ArtifactAssembly register(
48 ArtifactSlot slot,
49 TaskProvider<T> task,
50 Function<? super T, ? extends Provider<? extends FileSystemLocation>> mapOutputDirectory) {
51 if (assemblies.containsKey(slot)) {
52 throw new InvalidUserDataException("Artifact assembly '" + slot + "' is already registered");
53 }
54 var outputArtifact = task.flatMap(t -> mapOutputDirectory.apply(t));
55
56 var output = objects.fileCollection()
57 .from(outputArtifact)
58 .builtBy(task);
59
60 var assembly = new Assembly(outputArtifact, task, output);
61 assemblies.put(slot, assembly);
62 return assembly;
63 }
64
65 @Override
66 public Optional<ArtifactAssembly> find(ArtifactSlot slot) {
67 return Optional.ofNullable(assemblies.get(slot));
68 }
69
70 static class Assembly implements ArtifactAssembly {
71
72 private final Provider<? extends FileSystemLocation> artifact;
73 private final TaskProvider<? extends Task> task;
74 private final FileCollection fileCollection;
75
76 Assembly(Provider<? extends FileSystemLocation> artifact, TaskProvider<? extends Task> task,
77 FileCollection fileCollection) {
78 this.artifact = artifact;
79 this.task = task;
80 this.fileCollection = fileCollection;
81 }
82
83 @Override
84 public Provider<? extends FileSystemLocation> getArtifact() {
85 return artifact;
86 }
87
88 @Override
89 public TaskProvider<? extends Task> getAssemblyTask() {
90 return task;
91 }
92
93 @Override
94 public FileCollection getFileCollection() {
95 return fileCollection;
96 }
97
98 }
99 }
@@ -0,0 +1,45
1 package org.implab.gradle.variants.artifacts.internal;
2
3 import java.util.LinkedHashMap;
4 import java.util.Map;
5 import java.util.Optional;
6
7 import org.gradle.api.artifacts.ConfigurationContainer;
8 import org.gradle.api.model.ObjectFactory;
9 import org.gradle.api.provider.ProviderFactory;
10 import org.implab.gradle.variants.artifacts.OutgoingConfiguration;
11 import org.implab.gradle.variants.core.Variant;
12
13 public class OutgoingRegistry {
14 private final Map<Variant, DefaultOutgoingConfiguration> outgoingByVariant = new LinkedHashMap<>();
15
16 private final ConfigurationContainer configurations;
17 private final ObjectFactory objects;
18 private final ProviderFactory providers;
19
20 public OutgoingRegistry(ConfigurationContainer configurations, ObjectFactory objects, ProviderFactory providers) {
21 this.configurations = configurations;
22 this.objects = objects;
23 this.providers = providers;
24 }
25
26 public Optional<OutgoingConfiguration> findOutgoing(Variant variant) {
27 return Optional.ofNullable(outgoingByVariant.get(variant));
28 }
29
30 public OutgoingConfiguration maybeCreate(Variant variant) {
31 return outgoingByVariant.computeIfAbsent(variant, this::newOutgoingConfiguration);
32 }
33
34 private DefaultOutgoingConfiguration newOutgoingConfiguration(Variant variant) {
35 var configuration = configurations.register(outgoingConfigurationName(variant));
36
37 return new DefaultOutgoingConfiguration(variant, configuration, objects, providers);
38 }
39
40 String outgoingConfigurationName(Variant variant) {
41 return variant.getName() + "Elements";
42 }
43
44
45 }
@@ -1,64 +1,71
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import org.gradle.api.Action;
3 import org.gradle.api.Action;
4 import org.gradle.api.Plugin;
4 import org.gradle.api.Plugin;
5 import org.gradle.api.Project;
5 import org.gradle.api.Project;
6 import org.implab.gradle.common.core.lang.Deferred;
6 import org.implab.gradle.common.core.lang.Deferred;
7 import org.implab.gradle.variants.artifacts.ArtifactAssemblyRegistry;
7 import org.implab.gradle.variants.artifacts.OutgoingArtifactSlotSpec;
8 import org.implab.gradle.variants.artifacts.OutgoingArtifactSlotSpec;
8 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec;
9 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec;
9 import org.implab.gradle.variants.artifacts.VariantArtifactsContext;
10 import org.implab.gradle.variants.artifacts.VariantArtifactsContext;
10 import org.implab.gradle.variants.artifacts.VariantArtifactsExtension;
11 import org.implab.gradle.variants.artifacts.VariantArtifactsExtension;
11 import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
12 import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
12 import org.implab.gradle.variants.artifacts.internal.VariantArtifactsRegistry;
13 import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry;
13 import org.implab.gradle.variants.core.Variant;
14 import org.implab.gradle.variants.core.Variant;
14 import org.implab.gradle.variants.core.VariantsExtension;
15 import org.implab.gradle.variants.core.VariantsExtension;
15
16
16 public abstract class VariantArtifactsPlugin implements Plugin<Project> {
17 public abstract class VariantArtifactsPlugin implements Plugin<Project> {
17
18
18 @Override
19 @Override
19 public void apply(Project target) {
20 public void apply(Project target) {
20 var extensions = target.getExtensions();
21 var extensions = target.getExtensions();
21 var objects = target.getObjects();
22 var objects = target.getObjects();
23 var providers = target.getProviders();
24 var configurations = target.getConfigurations();
25 var tasks = target.getTasks();
22
26
23 // Apply the main VariantsPlugin to ensure the core variant model is available.
27 // Apply the main VariantsPlugin to ensure the core variant model is available.
24 target.getPlugins().apply(VariantsPlugin.class);
28 target.getPlugins().apply(VariantsPlugin.class);
25 // Access the VariantsExtension to configure variant sources.
29 // Access the VariantsExtension to configure variant sources.
26 var variantsExtension = extensions.getByType(VariantsExtension.class);
30 var variantsExtension = extensions.getByType(VariantsExtension.class);
27
31
28 var deferred = new Deferred<VariantArtifactsRegistry>();
32 var outgoing = new OutgoingRegistry(configurations, objects, providers);
33 var assemblies = new ArtifactAssemblyRegistry(objects, tasks);
34
35 var deferred = new Deferred<VariantArtifactsContext>();
29
36
30 variantsExtension.whenFinalized(variants -> {
37 variantsExtension.whenFinalized(variants -> {
31 deferred.resolve(new VariantArtifactsRegistry(variants));
38 deferred.resolve(new VariantArtifactsRegistry(variants));
32 });
39 });
33
40
34 var variantArtifacts = new VariantArtifactsExtension() {
41 var variantArtifacts = new VariantArtifactsExtension() {
35
42
36 @Override
43 @Override
37 public void variant(String variantName, Action<? super VariantArtifactsSpec> action) {
44 public void variant(String variantName, Action<? super VariantArtifactsSpec> action) {
38 deferred.whenResolved(registry -> registry.configureVariant(
45 deferred.whenResolved(context -> registry.configureVariant(
39 objects.named(Variant.class, variantName), action));
46 objects.named(Variant.class, variantName), action));
40 }
47 }
41
48
42 @Override
49 @Override
43 public void whenFinalized(Action<? super VariantArtifactsContext> action) {
50 public void whenFinalized(Action<? super VariantArtifactsContext> action) {
44 deferred.whenResolved(registry -> action.execute(registry.variantsContext()));
51 deferred.whenResolved(registry -> action.execute(registry.variantsContext()));
45 }
52 }
46
53
47 @Override
54 @Override
48 public void whenOutgoingVariant(Action<? super OutgoingConfigurationSpec> action) {
55 public void whenOutgoingVariant(Action<? super OutgoingConfigurationSpec> action) {
49 deferred.whenResolved(registry -> registry.configureOutgoing(action));
56 deferred.whenResolved(registry -> registry.configureOutgoing(action));
50
57
51 }
58 }
52
59
53 @Override
60 @Override
54 public void whenOutgoingSlot(Action<? super OutgoingArtifactSlotSpec> action) {
61 public void whenOutgoingSlot(Action<? super OutgoingArtifactSlotSpec> action) {
55 deferred.whenResolved(registry -> registry.configureOutgoingSlot(action));
62 deferred.whenResolved(registry -> registry.configureOutgoingSlot(action));
56 }
63 }
57
64
58 };
65 };
59
66
60 extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts);
67 extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts);
61
68
62 }
69 }
63
70
64 }
71 }
@@ -1,22 +1,33
1 package org.implab.gradle.variants.artifacts;
1 package org.implab.gradle.variants.artifacts;
2
2
3 import java.util.Optional;
4
3 import org.eclipse.jdt.annotation.NonNullByDefault;
5 import org.eclipse.jdt.annotation.NonNullByDefault;
6 import org.gradle.api.InvalidUserDataException;
4
7
5 /**
8 /**
6 * Resolves stateful slot assemblies from cheap slot identities.
9 * Resolves stateful slot assemblies from cheap slot identities.
7 *
10 *
8 * <p>The returned assembly is a materialized build-model handle. It may expose lazy Gradle providers, but
11 * <p>
12 * The returned assembly is a materialized build-model handle. It may expose
13 * lazy Gradle providers, but
9 * it is no longer an identity object suitable for replayable discovery.
14 * it is no longer an identity object suitable for replayable discovery.
10 */
15 */
11 @NonNullByDefault
16 @NonNullByDefault
12 public interface ArtifactAssemblies {
17 public interface ArtifactAssemblies {
13 /**
18 /**
14 * Resolves the assembly for the given slot.
19 * Resolves the assembly for the given slot.
15 *
20 *
16 * <p>This call materializes the build-facing body of the slot from its identity.
21 * <p>
22 * This call materializes the build-facing body of the slot from its identity.
17 *
23 *
18 * @param slot slot identity inside a variant outgoing contract
24 * @param slot slot identity inside a variant outgoing contract
19 * @return assembly handle for the slot
25 * @return assembly handle for the slot
20 */
26 */
21 ArtifactAssembly resolveSlot(ArtifactSlot slot);
27 default ArtifactAssembly require(ArtifactSlot slot) {
28 return find(slot)
29 .orElseThrow(() -> new InvalidUserDataException("Artifact assembly '" + slot + "' isn't registered"));
22 }
30 }
31
32 Optional<ArtifactAssembly> find(ArtifactSlot slot);
33 }
@@ -1,32 +1,43
1 package org.implab.gradle.variants.artifacts;
1 package org.implab.gradle.variants.artifacts;
2
2
3 import org.gradle.api.Task;
3 import org.gradle.api.Task;
4 import org.gradle.api.file.FileCollection;
4 import org.gradle.api.file.FileSystemLocation;
5 import org.gradle.api.file.FileSystemLocation;
5 import org.gradle.api.provider.Provider;
6 import org.gradle.api.provider.Provider;
6 import org.gradle.api.tasks.TaskProvider;
7 import org.gradle.api.tasks.TaskProvider;
7
8
8 /**
9 /**
9 * Materialized body of an {@link ArtifactSlot}.
10 * Materialized body of an {@link ArtifactSlot}.
10 *
11 *
11 * <p>An assembly is a stateful build object obtained on demand from
12 * <p>
12 * {@link ArtifactAssemblies#resolveSlot(ArtifactSlot)}. It describes how the slot artifact is produced and
13 * An assembly is a stateful build object obtained on demand from
14 * {@link ArtifactAssemblies#require(ArtifactSlot)}. It describes how the
15 * slot artifact is produced and
13 * where that single published artifact will appear.
16 * where that single published artifact will appear.
14 */
17 */
15 public interface ArtifactAssembly {
18 public interface ArtifactAssembly {
16
19
17 /**
20 /**
18 * Returns the published artifact produced for the slot.
21 * Returns the published artifact produced for the slot.
19 *
22 *
20 * <p>A slot is expected to produce exactly one artifact represented by one file or one directory.
23 * <p>
24 * A slot is expected to produce exactly one artifact represented by one file or
25 * one directory.
21 *
26 *
22 * @return provider of the produced artifact location
27 * @return provider of the produced artifact location
23 */
28 */
24 Provider<? extends FileSystemLocation> getArtifact();
29 Provider<? extends FileSystemLocation> getArtifact();
25
30
26 /**
31 /**
27 * Returns the task that assembles the slot artifact.
32 * Returns the task that assembles the slot artifact.
28 *
33 *
29 * @return provider of the assembly task
34 * @return provider of the assembly task
30 */
35 */
31 TaskProvider<? extends Task> getAssemblyTask();
36 TaskProvider<? extends Task> getAssemblyTask();
37
38 /**
39 * File collection, contains {@link #getArtifact()} and build dependency on
40 * {@link #getAssemblyTask()}. This is a conventional property.
41 */
42 FileCollection getFileCollection();
32 }
43 }
@@ -1,24 +1,17
1 package org.implab.gradle.variants.artifacts;
1 package org.implab.gradle.variants.artifacts;
2
2
3 /**
3 /**
4 * DSL model for selecting named outputs from a chosen source scope.
4 * DSL model for selecting named outputs from a chosen source scope.
5 *
5 *
6 * <p>The selected outputs are inputs to slot assembly. They do not create additional outgoing slots or
6 * <p>The selected outputs are inputs to slot assembly. They do not create additional outgoing slots or
7 * affect slot identity.
7 * affect slot identity.
8 */
8 */
9 public interface OutputSelectionSpec {
9 public interface OutputSelectionSpec {
10 /**
10 /**
11 * Selects one named output.
12 *
13 * @param name output name
14 */
15 void output(String name);
16
17 /**
18 * Selects several named outputs.
11 * Selects several named outputs.
19 *
12 *
20 * @param name first output name
13 * @param name first output name
21 * @param extra additional output names
14 * @param extra additional output names
22 */
15 */
23 void output(String name, String... extra);
16 void output(String name, String... extra);
24 }
17 }
@@ -1,34 +1,38
1 package org.implab.gradle.variants.artifacts;
1 package org.implab.gradle.variants.artifacts;
2
2
3 import java.util.Optional;
3 import java.util.Optional;
4
4
5 import org.gradle.api.Action;
5 import org.gradle.api.Action;
6 import org.gradle.api.InvalidUserDataException;
6 import org.implab.gradle.variants.core.Variant;
7 import org.implab.gradle.variants.core.Variant;
7 import org.implab.gradle.variants.core.VariantsView;
8 import org.implab.gradle.variants.core.VariantsView;
8
9
9 /**
10 /**
10 * Контекст работы с вариантами публикации, становится доступным после
11 * Контекст работы с вариантами публикации, становится доступным после
11 * финализации модели вариантов. Фактически является живой моделью
12 * финализации модели вариантов. Фактически является живой моделью
12 */
13 */
13 public interface VariantArtifactsContext {
14 public interface VariantArtifactsContext {
14
15
15 /**
16 /**
16 * Зафиксированное представление о вариантах на основе которого адаптеры могут
17 * Зафиксированное представление о вариантах на основе которого адаптеры могут
17 * конфигурировать артефакты и исходящие конфигурации
18 * конфигурировать артефакты и исходящие конфигурации
18 */
19 */
19 VariantsView getVariants();
20 VariantsView getVariants();
20
21
21 ArtifactAssemblies getAssemblies();
22 ArtifactAssemblies getAssemblies();
22
23
23 /**
24 /**
24 * Replayable hook для всех объявленных конфигураций
25 * Replayable hook для всех объявленных конфигураций
25 */
26 */
26 void all(Action<? super OutgoingConfiguration> action);
27 void all(Action<? super OutgoingConfiguration> action);
27
28
28 Optional<OutgoingConfiguration> findArtifacts(Variant variant);
29 Optional<OutgoingConfiguration> findOutgoing(Variant variant);
29
30
30 OutgoingConfiguration requireArtifacts(Variant variant);
31 default OutgoingConfiguration requireOutgoing(Variant variant) {
32 return findOutgoing(variant)
33 .orElseThrow(() -> new InvalidUserDataException("Outgoing variant '" + variant + "' isn't registered"));
34 }
31
35
32 ArtifactAssemblyRules slotRules(ArtifactSlot slot);
36 ArtifactAssemblyRules slotRules(ArtifactSlot slot);
33
37
34 }
38 }
@@ -1,52 +1,82
1 package org.implab.gradle.variants.artifacts.internal;
1 package org.implab.gradle.variants.artifacts.internal;
2
2
3 import java.util.HashSet;
4 import java.util.LinkedList;
5 import java.util.List;
3 import java.util.Set;
6 import java.util.Set;
7 import java.util.function.Consumer;
8 import java.util.stream.Stream;
4
9
5 import org.gradle.api.Action;
10 import org.gradle.api.Action;
6 import org.implab.gradle.variants.artifacts.ArtifactAssemblyRules;
11 import org.gradle.api.model.ObjectFactory;
12 import org.implab.gradle.common.core.lang.Strings;
7 import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec;
13 import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec;
8 import org.implab.gradle.variants.core.Variant;
14 import org.implab.gradle.variants.core.Layer;
15 import org.implab.gradle.variants.core.Role;
9 import org.implab.gradle.variants.artifacts.OutputSelectionSpec;
16 import org.implab.gradle.variants.artifacts.OutputSelectionSpec;
10
17
18 /**
19 * Реализация DSL модели, строит набор {@link SlotContribution}. При построении набора
20 * правила и корректность не проверяются. По окончании использования клиент
21 * вызывает метод {@link #process(Consumer)} для обработки результатов.
22 *
23 */
11 final class BoundArtifactAssemblySpec implements ArtifactAssemblySpec {
24 final class BoundArtifactAssemblySpec implements ArtifactAssemblySpec {
12 private final VariantArtifactsRegistry registry;
25 private final List<SlotContribution> contributions = new LinkedList<>();
13 private final Variant variant;
26 private final ObjectFactory objectFactory;
14 private final ArtifactAssemblyRules rules;
15
27
16 BoundArtifactAssemblySpec(
28 BoundArtifactAssemblySpec(ObjectFactory objectFactory) {
17 VariantArtifactsRegistry registry,
29 this.objectFactory = objectFactory;
18 Variant variant,
19 ArtifactAssemblyRules rules) {
20 this.registry = registry;
21 this.variant = variant;
22 this.rules = rules;
23 }
30 }
24
31
25 @Override
32 @Override
26 public void from(Object artifact) {
33 public void from(Object artifact) {
27 rules.addDirectInput(artifact);
34 contributions.add(new DirectContribution(artifact));
28 }
35 }
29
36
30 @Override
37 @Override
31 public void fromVariant(Action<? super OutputSelectionSpec> action) {
38 public void fromVariant(Action<? super OutputSelectionSpec> action) {
32 rules.addVariantOutputs(outputs(action));
39 contributions.add(new VariantOutputsContribution(outputs(action)));
33 }
40 }
34
41
35 @Override
42 @Override
36 public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) {
43 public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) {
37 var role = registry.requireRole(variant, roleName);
44
38 rules.addRoleOutputs(role, outputs(action));
45 contributions.add(new RoleOutputsContribution(
46 objectFactory.named(Role.class, roleName),
47 outputs(action)));
39 }
48 }
40
49
41 @Override
50 @Override
42 public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) {
51 public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) {
43 var layer = registry.requireLayer(variant, layerName);
52 contributions.add(new LayerOutputsContribution(
44 rules.addLayerOutputs(layer, outputs(action));
53 objectFactory.named(Layer.class, layerName),
54 outputs(action)));
55 }
56
57 void process(Consumer<? super SlotContribution> consumer) {
58 contributions.forEach(consumer);
45 }
59 }
46
60
47 private static Set<String> outputs(Action<? super OutputSelectionSpec> action) {
61 private static Set<String> outputs(Action<? super OutputSelectionSpec> action) {
48 var spec = new DefaultOutputSelectionSpec();
62 var spec = new OutputsSetSpec();
49 action.execute(spec);
63 action.execute(spec);
50 return spec.outputs();
64 return spec.outputs();
51 }
65 }
66
67 private static class OutputsSetSpec implements OutputSelectionSpec {
68 private final Set<String> outputs = new HashSet<>();
69
70 @Override
71 public void output(String name, String... extra) {
72 Stream.concat(Stream.of(name), Stream.of(extra))
73 .map(Strings::requireNonBlank)
74 .forEach(outputs::add);
52 }
75 }
76
77 Set<String> outputs() {
78 return Set.copyOf(outputs);
79 }
80
81 }
82 }
@@ -1,11 +1,17
1 package org.implab.gradle.variants.artifacts.internal;
1 package org.implab.gradle.variants.artifacts.internal;
2
2
3 import java.util.function.Consumer;
4
3 public interface SlotContributionVisitor {
5 public interface SlotContributionVisitor {
4 void visit(DirectContribution contribution);
6 void visit(DirectContribution contribution);
5
7
6 void visit(VariantOutputsContribution contribution);
8 void visit(VariantOutputsContribution contribution);
7
9
8 void visit(RoleOutputsContribution contribution);
10 void visit(RoleOutputsContribution contribution);
9
11
10 void visit(LayerOutputsContribution contribution);
12 void visit(LayerOutputsContribution contribution);
13
14 default Consumer<SlotContribution> consumer() {
15 return slot -> slot.accept(this);
11 }
16 }
17 }
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now