Search in sources :

Example 1 with TargetMachine

use of org.gradle.nativeplatform.TargetMachine in project gradle by gradle.

the class Dimensions method variants.

private static void variants(Provider<String> baseName, Collection<BuildType> buildTypes, Collection<TargetMachine> targetMachines, ObjectFactory objectFactory, ImmutableAttributesFactory attributesFactory, // TODO: These should come from somewhere else, probably
Provider<String> group, Provider<String> version, Action<NativeVariantIdentity> action) {
    for (BuildType buildType : buildTypes) {
        for (TargetMachine targetMachine : targetMachines) {
            Usage runtimeUsage = objectFactory.named(Usage.class, Usage.NATIVE_RUNTIME);
            List<String> variantNameToken = Lists.newArrayList();
            // FIXME: Always build type name to keep parity with previous Gradle version in tooling API
            variantNameToken.add(buildType.getName());
            variantNameToken.add(createDimensionSuffix(targetMachine.getOperatingSystemFamily(), targetMachinesToOperatingSystems(targetMachines)));
            variantNameToken.add(createDimensionSuffix(targetMachine.getArchitecture(), targetMachinesToArchitectures(targetMachines)));
            String variantName = StringUtils.uncapitalize(String.join("", variantNameToken));
            AttributeContainer runtimeAttributes = attributesFactory.mutable();
            runtimeAttributes.attribute(Usage.USAGE_ATTRIBUTE, runtimeUsage);
            addCommonAttributes(buildType, targetMachine, runtimeAttributes);
            DefaultUsageContext runtimeUsageContext = new DefaultUsageContext(variantName + "Runtime", runtimeAttributes);
            DefaultUsageContext linkUsageContext = null;
            NativeVariantIdentity variantIdentity = new NativeVariantIdentity(variantName, baseName, group, version, buildType.isDebuggable(), buildType.isOptimized(), targetMachine, linkUsageContext, runtimeUsageContext, null);
            action.execute(variantIdentity);
        }
    }
}
Also used : Usage(org.gradle.api.attributes.Usage) TargetMachine(org.gradle.nativeplatform.TargetMachine) DefaultUsageContext(org.gradle.language.cpp.internal.DefaultUsageContext) AttributeContainer(org.gradle.api.attributes.AttributeContainer) NativeVariantIdentity(org.gradle.language.cpp.internal.NativeVariantIdentity)

Example 2 with TargetMachine

use of org.gradle.nativeplatform.TargetMachine in project gradle by gradle.

the class Dimensions method variants.

private static void variants(Provider<String> baseName, Collection<BuildType> buildTypes, Collection<Linkage> linkages, Collection<TargetMachine> targetMachines, ObjectFactory objectFactory, ImmutableAttributesFactory attributesFactory, // TODO: These should come from somewhere else, probably
Provider<String> group, Provider<String> version, Action<NativeVariantIdentity> action) {
    for (BuildType buildType : buildTypes) {
        for (Linkage linkage : linkages) {
            for (TargetMachine targetMachine : targetMachines) {
                Usage runtimeUsage = objectFactory.named(Usage.class, Usage.NATIVE_RUNTIME);
                Usage linkUsage = objectFactory.named(Usage.class, Usage.NATIVE_LINK);
                List<String> variantNameToken = Lists.newArrayList();
                // FIXME: Always build type name to keep parity with previous Gradle version in tooling API
                variantNameToken.add(buildType.getName());
                variantNameToken.add(createDimensionSuffix(linkage, linkages));
                variantNameToken.add(createDimensionSuffix(targetMachine.getOperatingSystemFamily(), targetMachinesToOperatingSystems(targetMachines)));
                variantNameToken.add(createDimensionSuffix(targetMachine.getArchitecture(), targetMachinesToArchitectures(targetMachines)));
                String variantName = StringUtils.uncapitalize(String.join("", variantNameToken));
                AttributeContainer runtimeAttributes = attributesFactory.mutable();
                runtimeAttributes.attribute(Usage.USAGE_ATTRIBUTE, runtimeUsage);
                addCommonAttributes(buildType, targetMachine, runtimeAttributes);
                runtimeAttributes.attribute(LINKAGE_ATTRIBUTE, linkage);
                DefaultUsageContext runtimeUsageContext = new DefaultUsageContext(variantName + "Runtime", runtimeAttributes);
                AttributeContainer linkAttributes = attributesFactory.mutable();
                linkAttributes.attribute(Usage.USAGE_ATTRIBUTE, linkUsage);
                addCommonAttributes(buildType, targetMachine, linkAttributes);
                linkAttributes.attribute(LINKAGE_ATTRIBUTE, linkage);
                DefaultUsageContext linkUsageContext = new DefaultUsageContext(variantName + "Link", linkAttributes);
                NativeVariantIdentity variantIdentity = new NativeVariantIdentity(variantName, baseName, group, version, buildType.isDebuggable(), buildType.isOptimized(), targetMachine, linkUsageContext, runtimeUsageContext, linkage);
                action.execute(variantIdentity);
            }
        }
    }
}
Also used : Usage(org.gradle.api.attributes.Usage) Linkage(org.gradle.nativeplatform.Linkage) TargetMachine(org.gradle.nativeplatform.TargetMachine) DefaultUsageContext(org.gradle.language.cpp.internal.DefaultUsageContext) AttributeContainer(org.gradle.api.attributes.AttributeContainer) NativeVariantIdentity(org.gradle.language.cpp.internal.NativeVariantIdentity)

Example 3 with TargetMachine

use of org.gradle.nativeplatform.TargetMachine in project gradle by gradle.

the class XCTestConventionPlugin method apply.

@Override
public void apply(Project project) {
    project.getPluginManager().apply(SwiftBasePlugin.class);
    project.getPluginManager().apply(NativeTestingBasePlugin.class);
    final ProviderFactory providers = project.getProviders();
    // Create test suite component
    // TODO - Reuse logic from Swift*Plugin
    // TODO - component name and extension name aren't the same
    // TODO - should use `src/xctest/swift` as the convention?
    // Add the test suite and extension
    DefaultSwiftXCTestSuite testSuite = componentFactory.newInstance(SwiftXCTestSuite.class, DefaultSwiftXCTestSuite.class, "test");
    project.getExtensions().add(SwiftXCTestSuite.class, "xctest", testSuite);
    project.getComponents().add(testSuite);
    // Setup component
    testSuite.getModule().set(GUtil.toCamelCase(project.getName() + "Test"));
    final DefaultSwiftXCTestSuite testComponent = testSuite;
    testComponent.getTargetMachines().convention(useHostAsDefaultTargetMachine(targetMachineFactory));
    testComponent.getSourceCompatibility().convention(testComponent.getTestedComponent().flatMap(it -> it.getSourceCompatibility()));
    final String mainComponentName = "main";
    project.getComponents().withType(ProductionSwiftComponent.class, component -> {
        if (mainComponentName.equals(component.getName())) {
            testComponent.getTargetMachines().convention(component.getTargetMachines());
            testComponent.getTestedComponent().convention(component);
        }
    });
    testComponent.getTestBinary().convention(project.provider(() -> {
        return testComponent.getBinaries().get().stream().filter(SwiftXCTestBinary.class::isInstance).map(SwiftXCTestBinary.class::cast).findFirst().orElse(null);
    }));
    testComponent.getBinaries().whenElementKnown(DefaultSwiftXCTestBinary.class, binary -> {
        // Create test suite test task
        TaskProvider<XCTest> testingTask = project.getTasks().register("xcTest", XCTest.class, task -> {
            task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
            task.setDescription("Executes XCTest suites");
            task.getTestInstallDirectory().set(binary.getInstallDirectory());
            task.getRunScriptFile().set(binary.getRunScriptFile());
            task.getWorkingDirectory().set(binary.getInstallDirectory());
        });
        binary.getRunTask().set(testingTask);
        configureTestSuiteBuildingTasks(project, binary);
        configureTestSuiteWithTestedComponentWhenAvailable(project, testComponent, binary);
    });
    project.afterEvaluate(p -> {
        final SwiftComponent mainComponent = testComponent.getTestedComponent().getOrNull();
        final SetProperty<TargetMachine> mainTargetMachines = mainComponent != null ? mainComponent.getTargetMachines() : null;
        Dimensions.unitTestVariants(testComponent.getModule(), testComponent.getTargetMachines(), mainTargetMachines, objectFactory, attributesFactory, providers.provider(() -> project.getGroup().toString()), providers.provider(() -> project.getVersion().toString()), variantIdentity -> {
            if (tryToBuildOnHost(variantIdentity)) {
                testComponent.getSourceCompatibility().finalizeValue();
                ToolChainSelector.Result<SwiftPlatform> result = toolChainSelector.select(SwiftPlatform.class, new DefaultSwiftPlatform(variantIdentity.getTargetMachine(), testComponent.getSourceCompatibility().getOrNull()));
                // Create test suite executable
                if (result.getTargetPlatform().getTargetMachine().getOperatingSystemFamily().isMacOs()) {
                    testComponent.addBundle(variantIdentity, result.getTargetPlatform(), result.getToolChain(), result.getPlatformToolProvider());
                } else {
                    testComponent.addExecutable(variantIdentity, result.getTargetPlatform(), result.getToolChain(), result.getPlatformToolProvider());
                }
            }
        });
        testComponent.getBinaries().realizeNow();
    });
}
Also used : ConfigurableFileCollection(org.gradle.api.file.ConfigurableFileCollection) Arrays(java.util.Arrays) UnexportMainSymbol(org.gradle.language.nativeplatform.tasks.UnexportMainSymbol) NativeToolChainRegistryInternal(org.gradle.nativeplatform.toolchain.internal.NativeToolChainRegistryInternal) ProductionSwiftComponent(org.gradle.language.swift.ProductionSwiftComponent) DefaultSwiftXCTestExecutable(org.gradle.nativeplatform.test.xctest.internal.DefaultSwiftXCTestExecutable) GUtil(org.gradle.util.internal.GUtil) Provider(org.gradle.api.provider.Provider) ModelRegistry(org.gradle.model.internal.registry.ModelRegistry) DefaultNativePlatform(org.gradle.nativeplatform.platform.internal.DefaultNativePlatform) DefaultSwiftXCTestSuite(org.gradle.nativeplatform.test.xctest.internal.DefaultSwiftXCTestSuite) TaskProvider(org.gradle.api.tasks.TaskProvider) XCTest(org.gradle.nativeplatform.test.xctest.tasks.XCTest) TargetMachineFactory(org.gradle.nativeplatform.TargetMachineFactory) SwiftXCTestBundle(org.gradle.nativeplatform.test.xctest.SwiftXCTestBundle) ProjectInternal(org.gradle.api.internal.project.ProjectInternal) SwiftBasePlugin(org.gradle.language.swift.plugins.SwiftBasePlugin) PlatformToolProvider(org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider) ImmutableAttributesFactory(org.gradle.api.internal.attributes.ImmutableAttributesFactory) Project(org.gradle.api.Project) TargetMachine(org.gradle.nativeplatform.TargetMachine) Dimensions(org.gradle.language.nativeplatform.internal.Dimensions) ToolChainSelector(org.gradle.language.nativeplatform.internal.toolchains.ToolChainSelector) MacOSSdkPlatformPathLocator(org.gradle.nativeplatform.toolchain.internal.xcode.MacOSSdkPlatformPathLocator) TaskContainer(org.gradle.api.tasks.TaskContainer) Names(org.gradle.language.nativeplatform.internal.Names) NativeToolChain(org.gradle.nativeplatform.toolchain.NativeToolChain) SwiftPlatform(org.gradle.language.swift.SwiftPlatform) SwiftXCTestBinary(org.gradle.nativeplatform.test.xctest.SwiftXCTestBinary) DefaultSwiftXCTestBinary(org.gradle.nativeplatform.test.xctest.internal.DefaultSwiftXCTestBinary) NativeTestingBasePlugin(org.gradle.nativeplatform.test.plugins.NativeTestingBasePlugin) Dimensions.useHostAsDefaultTargetMachine(org.gradle.language.nativeplatform.internal.Dimensions.useHostAsDefaultTargetMachine) Dimensions.tryToBuildOnHost(org.gradle.language.nativeplatform.internal.Dimensions.tryToBuildOnHost) Inject(javax.inject.Inject) LifecycleBasePlugin(org.gradle.language.base.plugins.LifecycleBasePlugin) Lists(com.google.common.collect.Lists) Sync(org.gradle.api.tasks.Sync) LinkMachOBundle(org.gradle.nativeplatform.tasks.LinkMachOBundle) DefaultSwiftPlatform(org.gradle.language.swift.internal.DefaultSwiftPlatform) NativeComponentFactory(org.gradle.language.internal.NativeComponentFactory) SwiftApplication(org.gradle.language.swift.SwiftApplication) SwiftComponent(org.gradle.language.swift.SwiftComponent) NativeToolChainInternal(org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal) SwiftCompile(org.gradle.language.swift.tasks.SwiftCompile) File(java.io.File) SetProperty(org.gradle.api.provider.SetProperty) ProviderFactory(org.gradle.api.provider.ProviderFactory) DefaultSwiftBinary(org.gradle.language.swift.internal.DefaultSwiftBinary) ObjectFactory(org.gradle.api.model.ObjectFactory) DefaultSwiftXCTestBundle(org.gradle.nativeplatform.test.xctest.internal.DefaultSwiftXCTestBundle) RegularFile(org.gradle.api.file.RegularFile) InstallXCTestBundle(org.gradle.nativeplatform.test.xctest.tasks.InstallXCTestBundle) Dependency(org.gradle.api.artifacts.Dependency) Plugin(org.gradle.api.Plugin) SwiftXCTestSuite(org.gradle.nativeplatform.test.xctest.SwiftXCTestSuite) DefaultSwiftPlatform(org.gradle.language.swift.internal.DefaultSwiftPlatform) SwiftXCTestBinary(org.gradle.nativeplatform.test.xctest.SwiftXCTestBinary) DefaultSwiftXCTestBinary(org.gradle.nativeplatform.test.xctest.internal.DefaultSwiftXCTestBinary) TargetMachine(org.gradle.nativeplatform.TargetMachine) Dimensions.useHostAsDefaultTargetMachine(org.gradle.language.nativeplatform.internal.Dimensions.useHostAsDefaultTargetMachine) ProviderFactory(org.gradle.api.provider.ProviderFactory) DefaultSwiftXCTestSuite(org.gradle.nativeplatform.test.xctest.internal.DefaultSwiftXCTestSuite) ToolChainSelector(org.gradle.language.nativeplatform.internal.toolchains.ToolChainSelector) ProductionSwiftComponent(org.gradle.language.swift.ProductionSwiftComponent) SwiftComponent(org.gradle.language.swift.SwiftComponent) XCTest(org.gradle.nativeplatform.test.xctest.tasks.XCTest) SwiftPlatform(org.gradle.language.swift.SwiftPlatform) DefaultSwiftPlatform(org.gradle.language.swift.internal.DefaultSwiftPlatform)

Example 4 with TargetMachine

use of org.gradle.nativeplatform.TargetMachine in project gradle by gradle.

the class CppUnitTestPlugin method apply.

@Override
public void apply(final Project project) {
    project.getPluginManager().apply(CppBasePlugin.class);
    project.getPluginManager().apply(NativeTestingBasePlugin.class);
    final ProviderFactory providers = project.getProviders();
    final TaskContainer tasks = project.getTasks();
    // Add the unit test and extension
    final DefaultCppTestSuite testComponent = componentFactory.newInstance(CppTestSuite.class, DefaultCppTestSuite.class, "test");
    project.getExtensions().add(CppTestSuite.class, "unitTest", testComponent);
    project.getComponents().add(testComponent);
    testComponent.getBaseName().convention(project.getName() + "Test");
    testComponent.getTargetMachines().convention(Dimensions.useHostAsDefaultTargetMachine(targetMachineFactory));
    final String mainComponentName = "main";
    project.getComponents().withType(ProductionCppComponent.class, component -> {
        if (mainComponentName.equals(component.getName())) {
            testComponent.getTargetMachines().convention(component.getTargetMachines());
            testComponent.getTestedComponent().convention(component);
        }
    });
    testComponent.getTestBinary().convention(project.provider(new Callable<CppTestExecutable>() {

        @Override
        public CppTestExecutable call() throws Exception {
            return getAllBuildableTestExecutable().filter(it -> isCurrentArchitecture(it.getNativePlatform())).findFirst().orElse(getAllBuildableTestExecutable().findFirst().orElse(getAllTestExecutable().findFirst().orElse(null)));
        }

        private boolean isCurrentArchitecture(NativePlatform targetPlatform) {
            return targetPlatform.getArchitecture().equals(DefaultNativePlatform.getCurrentArchitecture());
        }

        private Stream<DefaultCppTestExecutable> getAllBuildableTestExecutable() {
            return getAllTestExecutable().filter(it -> it.getPlatformToolProvider().isAvailable());
        }

        private Stream<DefaultCppTestExecutable> getAllTestExecutable() {
            return testComponent.getBinaries().get().stream().filter(CppTestExecutable.class::isInstance).map(DefaultCppTestExecutable.class::cast);
        }
    }));
    testComponent.getBinaries().whenElementKnown(DefaultCppTestExecutable.class, binary -> {
        // TODO: Replace with native test task
        final TaskProvider<RunTestExecutable> testTask = tasks.register(binary.getNames().getTaskName("run"), RunTestExecutable.class, task -> {
            task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
            task.setDescription("Executes C++ unit tests.");
            final InstallExecutable installTask = binary.getInstallTask().get();
            task.onlyIf(element -> binary.getInstallDirectory().get().getAsFile().exists());
            task.getInputs().dir(binary.getInstallDirectory()).withPropertyName("installDirectory");
            task.setExecutable(installTask.getRunScriptFile().get().getAsFile());
            task.dependsOn(binary.getInstallDirectory());
            // TODO: Honor changes to build directory
            task.setOutputDir(project.getLayout().getBuildDirectory().dir("test-results/" + binary.getNames().getDirName()).get().getAsFile());
        });
        binary.getRunTask().set(testTask);
        configureTestSuiteWithTestedComponentWhenAvailable(project, testComponent, binary);
    });
    project.afterEvaluate(p -> {
        final CppComponent mainComponent = testComponent.getTestedComponent().getOrNull();
        final SetProperty<TargetMachine> mainTargetMachines = mainComponent != null ? mainComponent.getTargetMachines() : null;
        Dimensions.unitTestVariants(testComponent.getBaseName(), testComponent.getTargetMachines(), mainTargetMachines, objectFactory, attributesFactory, providers.provider(() -> project.getGroup().toString()), providers.provider(() -> project.getVersion().toString()), variantIdentity -> {
            if (tryToBuildOnHost(variantIdentity)) {
                ToolChainSelector.Result<CppPlatform> result = toolChainSelector.select(CppPlatform.class, new DefaultCppPlatform(variantIdentity.getTargetMachine()));
                // TODO: Removing `debug` from variant name to keep parity with previous Gradle version in tooling models
                CppTestExecutable testExecutable = testComponent.addExecutable(variantIdentity.getName().replace("debug", ""), variantIdentity, result.getTargetPlatform(), result.getToolChain(), result.getPlatformToolProvider());
            }
        });
        // TODO: Publishing for test executable?
        testComponent.getBinaries().realizeNow();
    });
}
Also used : Callable(java.util.concurrent.Callable) RunTestExecutable(org.gradle.nativeplatform.test.tasks.RunTestExecutable) DefaultCppTestExecutable(org.gradle.nativeplatform.test.cpp.internal.DefaultCppTestExecutable) CppPlatform(org.gradle.language.cpp.CppPlatform) DefaultCppPlatform(org.gradle.language.cpp.internal.DefaultCppPlatform) TaskContainer(org.gradle.api.tasks.TaskContainer) InstallExecutable(org.gradle.nativeplatform.tasks.InstallExecutable) CppComponent(org.gradle.language.cpp.CppComponent) ProductionCppComponent(org.gradle.language.cpp.ProductionCppComponent) TargetMachine(org.gradle.nativeplatform.TargetMachine) ProviderFactory(org.gradle.api.provider.ProviderFactory) DefaultCppPlatform(org.gradle.language.cpp.internal.DefaultCppPlatform) NativePlatform(org.gradle.nativeplatform.platform.NativePlatform) DefaultNativePlatform(org.gradle.nativeplatform.platform.internal.DefaultNativePlatform) ToolChainSelector(org.gradle.language.nativeplatform.internal.toolchains.ToolChainSelector) DefaultCppTestExecutable(org.gradle.nativeplatform.test.cpp.internal.DefaultCppTestExecutable) CppTestExecutable(org.gradle.nativeplatform.test.cpp.CppTestExecutable) DefaultCppTestSuite(org.gradle.nativeplatform.test.cpp.internal.DefaultCppTestSuite)

Example 5 with TargetMachine

use of org.gradle.nativeplatform.TargetMachine in project gradle by gradle.

the class NativeTestingBasePlugin method apply.

@Override
public void apply(final Project project) {
    project.getPluginManager().apply(LifecycleBasePlugin.class);
    project.getPluginManager().apply(NativeBasePlugin.class);
    project.getPluginManager().apply(TestingBasePlugin.class);
    // Create test lifecycle task
    TaskContainer tasks = project.getTasks();
    final TaskProvider<Task> test = tasks.register(TEST_TASK_NAME, task -> task.dependsOn((Callable<Object>) () -> {
        TestSuiteComponent unitTestSuite = project.getComponents().withType(TestSuiteComponent.class).findByName(TEST_COMPONENT_NAME);
        if (unitTestSuite != null && unitTestSuite.getTestBinary().isPresent()) {
            return unitTestSuite.getTestBinary().get().getRunTask();
        }
        return null;
    }));
    project.getComponents().withType(TestSuiteComponent.class, testSuiteComponent -> {
        if (testSuiteComponent instanceof ComponentWithTargetMachines) {
            ComponentWithTargetMachines componentWithTargetMachines = (ComponentWithTargetMachines) testSuiteComponent;
            if (TEST_COMPONENT_NAME.equals(testSuiteComponent.getName())) {
                test.configure(task -> task.dependsOn((Callable) () -> {
                    TargetMachine currentHost = ((DefaultTargetMachineFactory) targetMachineFactory).host();
                    boolean targetsCurrentMachine = componentWithTargetMachines.getTargetMachines().get().stream().anyMatch(targetMachine -> currentHost.getOperatingSystemFamily().equals(targetMachine.getOperatingSystemFamily()));
                    if (!targetsCurrentMachine) {
                        task.getLogger().warn("'" + testSuiteComponent.getName() + "' component in project '" + project.getPath() + "' does not target this operating system.");
                    }
                    return Collections.emptyList();
                }));
            }
        }
    });
    tasks.named(LifecycleBasePlugin.CHECK_TASK_NAME, task -> task.dependsOn(test));
}
Also used : Task(org.gradle.api.Task) TaskContainer(org.gradle.api.tasks.TaskContainer) ComponentWithTargetMachines(org.gradle.language.ComponentWithTargetMachines) TargetMachine(org.gradle.nativeplatform.TargetMachine) Callable(java.util.concurrent.Callable) TestSuiteComponent(org.gradle.nativeplatform.test.TestSuiteComponent) DefaultTargetMachineFactory(org.gradle.nativeplatform.internal.DefaultTargetMachineFactory)

Aggregations

TargetMachine (org.gradle.nativeplatform.TargetMachine)6 TaskContainer (org.gradle.api.tasks.TaskContainer)4 Callable (java.util.concurrent.Callable)3 AttributeContainer (org.gradle.api.attributes.AttributeContainer)3 Usage (org.gradle.api.attributes.Usage)3 Inject (javax.inject.Inject)2 Plugin (org.gradle.api.Plugin)2 Project (org.gradle.api.Project)2 Task (org.gradle.api.Task)2 RegularFile (org.gradle.api.file.RegularFile)2 ObjectFactory (org.gradle.api.model.ObjectFactory)2 Provider (org.gradle.api.provider.Provider)2 ProviderFactory (org.gradle.api.provider.ProviderFactory)2 TaskProvider (org.gradle.api.tasks.TaskProvider)2 ComponentWithTargetMachines (org.gradle.language.ComponentWithTargetMachines)2 LifecycleBasePlugin (org.gradle.language.base.plugins.LifecycleBasePlugin)2 DefaultUsageContext (org.gradle.language.cpp.internal.DefaultUsageContext)2 NativeVariantIdentity (org.gradle.language.cpp.internal.NativeVariantIdentity)2 Names (org.gradle.language.nativeplatform.internal.Names)2 ToolChainSelector (org.gradle.language.nativeplatform.internal.toolchains.ToolChainSelector)2