Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot change dependencies of dependency configuration ':app:implementation' after it has been included in dependency resolution. #18

Closed
manza7014 opened this issue Dec 20, 2021 · 29 comments
Labels
bug Something isn't working good first issue Good for newcomers

Comments

@manza7014
Copy link

manza7014 commented Dec 20, 2021

步骤
1.首次运行正常
2.再次运行报错,日志如下
另:在另外一个项目上运行正常
@trycatchx

Exception is:
org.gradle.api.InvalidUserDataException: Cannot change dependencies of dependency configuration ':app:implementation' after it has been included in dependency resolution.
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.preventIllegalMutation(DefaultConfiguration.java:1137)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.validateMutation(DefaultConfiguration.java:1099)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.lambda$validateMutationType$1(DefaultConfiguration.java:298)
	at org.gradle.internal.ImmutableActionSet$SingletonSet.execute(ImmutableActionSet.java:225)
	at org.gradle.api.internal.DefaultDomainObjectSet.assertMutableCollectionContents(DefaultDomainObjectSet.java:71)
	at org.gradle.api.internal.DefaultDomainObjectCollection$IteratorImpl.remove(DefaultDomainObjectCollection.java:486)
	at kotlin.collections.CollectionsKt__MutableCollectionsKt.filterInPlace$CollectionsKt__MutableCollectionsKt(MutableCollections.kt:220)
	at kotlin.collections.CollectionsKt__MutableCollectionsKt.removeAll(MutableCollections.kt:206)
	at plugin.utils.DependenciesHelper.modifyDependencies(DependenciesHelper.kt:80)
	at plugin.ChildProjectDependencies.doDependencies(ChildProjectDependencies.kt:64)
	at plugin.AppProjectDependencies.resolveDenpendency(AppProjectDependencies.kt:46)
	at plugin.AppProjectDependencies._init_$lambda-0(AppProjectDependencies.kt:30)
@trycatchx
Copy link
Owner

检查一下 是否配置 org.gradle.configureondemand = true ,关闭即可

@manza7014
Copy link
Author

检查一下 是否配置 org.gradle.configureondemand = true ,关闭即可

没有配置这个

@trycatchx
Copy link
Owner

目前使用gradle 版本是多少?

@manza7014
Copy link
Author

目前使用gradle 版本是多少?

gradle版本:6.5
AGP版本:4.0.2

@trycatchx
Copy link
Owner

trycatchx commented Dec 20, 2021

目前一个项目可以正常运行,另一个不行是吧。问题是修改依赖的时机慢导致的。

你可以通过本地依赖:加入以下代码

buildscript {
    repositories {
        mavenCentral()
        google()
    }
    dependencies {
        def pluginDir = getProjectDir().getAbsolutePath() + File.separator + "zPlugin"
        //编译加速插件,需要去插件市场搜索 RockectX 并开启可以加速项目编译速度
        def rockectxFile = new File(pluginDir,"rockectx.jar")
        classpath files(rockectxFile)
    }
}

项目根目录创建一下 zPlugin 文件夹,解压下面的 zip 文件拿到本地的 jar 放进 zPlugin 文件夹
rockectx.jar.zip

上面的本地 jar 主要是剔除了 并行的开关。定位是不是并行的原因导致的

@manza7014
Copy link
Author

好的 ,我试下

@manza7014
Copy link
Author

目前一个项目可以正常运行,另一个不行是吧。问题是修改依赖的时机慢导致的。

你可以通过本地依赖:加入以下代码

buildscript {
    repositories {
        mavenCentral()
        google()
    }
    dependencies {
        def pluginDir = getProjectDir().getAbsolutePath() + File.separator + "zPlugin"
        //编译加速插件,需要去插件市场搜索 RockectX 并开启可以加速项目编译速度
        def rockectxFile = new File(pluginDir,"rockectx.jar")
        classpath files(rockectxFile)
    }
}

项目根目录创建一下 zPlugin 文件夹,解压下面的 zip 文件拿到本地的 jar 放进 zPlugin 文件夹 rockectx.jar.zip

上面的本地 jar 主要是剔除了 并行的开关。定位是不是并行的原因导致的

步骤:
1.添加rockectx本地依赖并同步
2.执行RocketXClean
3.首次run正常
4.再次run还是同样的问题

@manza7014
Copy link
Author

gradle这一块不太懂,github上扒了下这个
//The configuration has been used in a resolution, and it is an error for build logic to change any dependencies,
// exclude rules or parent configurations (values that will affect the resolved graph).

private void preventIllegalMutation(MutationType type) {
        // TODO: Deprecate and eventually prevent these mutations when already resolved
        if (type == MutationType.DEPENDENCY_ATTRIBUTES) {
            return;
        }

        InternalState resolvedState = currentResolveState.get().state;
        if (resolvedState == ARTIFACTS_RESOLVED) {
            // The public result for the configuration has been calculated.
            // It is an error to change anything that would change the dependencies or artifacts
            throw new InvalidUserDataException(String.format("Cannot change %s of dependency %s after it has been resolved.", type, getDisplayName()));
        } else if (resolvedState == GRAPH_RESOLVED) {
            // The task dependencies for the configuration have been calculated using Configuration.getBuildDependencies().
            throw new InvalidUserDataException(String.format("Cannot change %s of dependency %s after task dependencies have been resolved", type, getDisplayName()));
        } else if (observedState == GRAPH_RESOLVED || observedState == ARTIFACTS_RESOLVED) {
            // The configuration has been used in a resolution, and it is an error for build logic to change any dependencies,
            // exclude rules or parent configurations (values that will affect the resolved graph).
            if (type != MutationType.STRATEGY) {
                String extraMessage = insideBeforeResolve ? " Use 'defaultDependencies' instead of 'beforeResolve' to specify default dependencies for a configuration." : "";
                throw new InvalidUserDataException(String.format("Cannot change %s of dependency %s after it has been included in dependency resolution.%s", type, getDisplayName(), extraMessage));
            }
        }
    }

@trycatchx
Copy link
Owner

@manza7014 好的,那应该和这个没有关系。应该还是兼容性问题。目前我排查到的位置是:
//步骤1:入口 DefaultDomainObjectCollection.IteratorImpl.class

  public void remove() {
           //..
            DefaultDomainObjectCollection.this.assertMutableCollectionContents();  // 此处报错
            this.iterator.remove();
          //..
        }

//步骤2:调用 DefaultDomainObjectSet.java


    protected void assertMutableCollectionContents() {
        this.beforeContainerChange.execute((Object)null);
    }

//步骤3: beforeContainerChange 是 下面的 action

    private static Action<Void> validateMutationType(final MutationValidator mutationValidator, final MutationType type) {
        return new Action<Void>() {
            public void execute(Void arg) {
                mutationValidator.validateMutation(type); //检查都否修改依赖
            }
        };
    }

//步骤4 :报错位置:

      public void validateMutation(MutationType type) {
        this.preventIllegalMutation(type);
        this.markAsModified(type);
        this.notifyChildren(type);
    }

    private void preventIllegalMutation(MutationType type) {
        if (type != MutationType.DEPENDENCY_ATTRIBUTES) {
            if (this.resolvedState == InternalState.ARTIFACTS_RESOLVED) {
                throw new InvalidUserDataException(String.format("Cannot change %s of dependency %s after it has been resolved.", type, this.getDisplayName()));
            } else if (this.resolvedState == InternalState.GRAPH_RESOLVED) {
                throw new InvalidUserDataException(String.format("Cannot change %s of dependency %s after task dependencies have been resolved", type, this.getDisplayName()));
            } else if ((this.observedState == InternalState.GRAPH_RESOLVED || this.observedState == InternalState.ARTIFACTS_RESOLVED) && type != MutationType.STRATEGY) {
                String extraMessage = this.insideBeforeResolve ? " Use 'defaultDependencies' instead of 'beforeResolve' to specify default dependencies for a configuration." : "";
                throw new InvalidUserDataException(String.format("Cannot change %s of dependency %s after it has been included in dependency resolution.%s", type, this.getDisplayName(), extraMessage));
            }
        }
    }

@trycatchx
Copy link
Owner

@manza7014 可能我需要在 步骤2 反射 beforeContainerChange 这个变量为: ImmutableActionSet.empty(); ,然后再执行 :

   parentConfig.dependencies.removeAll { dependency ->
                    dependency is DefaultProjectDependency && dependency.name.equals(projectWapper.project.name)
                }

才能摆脱这个 异常。目前根据gradle 源码查看 去除检查 应该是可行:
image

@trycatchx
Copy link
Owner

trycatchx commented Dec 20, 2021

@manza7014 有兴趣帮忙提个 pr 吗?修改完毕之后并可以在你的工程检验一下效果。
如果没有时间的话,需要等待我们这边进行修复。

@manza7014
Copy link
Author

之前还没提过pr 😳,我试试

@trycatchx
Copy link
Owner

@manza7014 好的你可以先尝试一下,非常感谢

@manza7014
Copy link
Author

@manza7014 好的你可以先尝试一下,非常感谢
客气,非常感谢作者这个RocketXPlugin,体验了,效果非常显著,👍

@trycatchx
Copy link
Owner

@manza7014

好的,关键的提示:

修改 DependenciesHelper.kt 类下面的:

    fun modifyDependencies(projectWapper: ChildProjectDependencies) {
             /**
              *这里加入 反射   parentConfig.dependencies 变量(它是一个 DefaultDomainObjectSet 对象,直接强转)
              * 并拿到 beforeContainerChange 变量赋值为 ImmutableActionSet.empty() (记得把变量的原有值保存)
             */

              // 剔除原有的依赖
                parentConfig.dependencies.removeAll { dependency ->
                    dependency is DefaultProjectDependency && dependency.name.equals(projectWapper.project.name)
                }

             // removeAll 操作完毕之后,记得 把原有值 赋值回去 (不赋值回去也问题不大)
          
    }

@trycatchx
Copy link
Owner

@manza7014 请问有进展吗?

@manza7014
Copy link
Author

manza7014 commented Dec 21, 2021

还没。

parentConfig.dependencies 变量(它是一个 DefaultDomainObjectSet 对象,直接强转)

这里有问题,强转失败,提示类型不对。
parentConfig.dependencies类型应该是DefaultDependencySet。
是不是拿到DefaultDependencySet父类DelegatingDomainObjectSet中的私有属性backingSet(类型:DomainObjectSet,子类DefaultDomainObjectSet)才能修改?
试了试,调试过程中有点问题,今天忙着项目和年报总结就没搞了,想着明天再接着搞呢

调试有点艰难,我每次打了jar包才能试一下,暂时还没有把我的项目app module以及其他module放到RocketXPlugin项目中

@trycatchx
Copy link
Owner

@manza7014 你可以把buildSrc 拷贝到你的项目中,然后直接剔除之前的 classpath 依赖,即可调试。

@trycatchx
Copy link
Owner

trycatchx commented Dec 21, 2021

还没。

parentConfig.dependencies 变量(它是一个 DefaultDomainObjectSet 对象,直接强转)

这里有问题,强转失败,提示类型不对。 parentConfig.dependencies类型应该是DefaultDependencySet。 是不是拿到DefaultDependencySet父类DelegatingDomainObjectSet中的私有属性backingSet(类型:DomainObjectSet,子类DefaultDomainObjectSet)才能修改? 试了试,调试过程中有点问题,今天忙着项目和年报总结就没搞了,想着明天再接着搞呢

调试有点艰难,我每次打了jar包才能试一下,暂时还没有把我的项目app module以及其他module放到RocketXPlugin项目中

是的,我看了一下确实是要通过拿到 DelegatingDomainObjectSet 的私有变量 backingSet (DefaultDomainObjectSet )

@manza7014
Copy link
Author

@manza7014 你可以把buildSrc 拷贝到你的项目中,然后直接剔除之前的 classpath 依赖,即可调试。

好的

@trycatchx
Copy link
Owner

trycatchx commented Dec 21, 2021

@manza7014 我编写了 setBeforeContainerChangeToEmpty 方法,你有空在你的 工程试试。

    fun modifyDependencies(projectWapper: ChildProjectDependencies) {
               //..
                setBeforeContainerChangeToEmpty(parentConfig.dependencies)
                // 剔除原有的依赖
                parentConfig.dependencies.removeAll { dependency ->
                    dependency is DefaultProjectDependency && dependency.name.equals(projectWapper.project.name)
                }
            // ..
 }

    fun setBeforeContainerChangeToEmpty(dependency :DependencySet):ImmutableActionSet<Any>? {
        var ret : ImmutableActionSet<Any>? = null
        try {
            val fBackingSet = DelegatingDomainObjectSet::class.java.getDeclaredField("backingSet")
            fBackingSet.isAccessible = true
            val domainObjectSet =  fBackingSet.get(dependency)
            val fBeforeContainerChange = DefaultDomainObjectSet::class.java.getDeclaredField("beforeContainerChange")
            fBeforeContainerChange.isAccessible = true
            ret = fBeforeContainerChange.get(domainObjectSet) as? ImmutableActionSet<Any>
            fBeforeContainerChange.set(domainObjectSet, ImmutableActionSet.empty<Any>())
        }catch (ignore:Exception) {
        }
        return ret
    }

@manza7014
Copy link
Author

还没。

parentConfig.dependencies 变量(它是一个 DefaultDomainObjectSet 对象,直接强转)

这里有问题,强转失败,提示类型不对。 parentConfig.dependencies类型应该是DefaultDependencySet。 是不是拿到DefaultDependencySet父类DelegatingDomainObjectSet中的私有属性backingSet(类型:DomainObjectSet,子类DefaultDomainObjectSet)才能修改? 试了试,调试过程中有点问题,今天忙着项目和年报总结就没搞了,想着明天再接着搞呢
调试有点艰难,我每次打了jar包才能试一下,暂时还没有把我的项目app module以及其他module放到RocketXPlugin项目中

是的,我看了一下确实是要通过拿到 DelegatingDomainObjectSet 的私有变量 backingSet (DefaultDomainObjectSet )

关键是拿不到:no_mouth:

Can not set final org.gradle.api.DomainObjectSet field org.gradle.api.internal.DelegatingDomainObjectSet.backingSet to java.lang.reflect.Field

@trycatchx
Copy link
Owner

你尝试用我的方法,我刚刚试了好像没问题哦

@manza7014
Copy link
Author

@manza7014 我编写了 setBeforeContainerChangeToEmpty 方法,你有空在你的 工程试试。

    fun modifyDependencies(projectWapper: ChildProjectDependencies) {
               //..
                setBeforeContainerChangeToEmpty(parentConfig.dependencies)
                // 剔除原有的依赖
                parentConfig.dependencies.removeAll { dependency ->
                    dependency is DefaultProjectDependency && dependency.name.equals(projectWapper.project.name)
                }
            // ..
 }

    fun setBeforeContainerChangeToEmpty(dependency :DependencySet):ImmutableActionSet<Any>? {
        var ret : ImmutableActionSet<Any>? = null
        try {
            val fBackingSet = DelegatingDomainObjectSet::class.java.getDeclaredField("backingSet")
            fBackingSet.isAccessible = true
            val domainObjectSet =  fBackingSet.get(dependency)
            val fBeforeContainerChange = DefaultDomainObjectSet::class.java.getDeclaredField("beforeContainerChange")
            fBeforeContainerChange.isAccessible = true
            ret = fBeforeContainerChange.get(domainObjectSet) as? ImmutableActionSet<Any>
            fBeforeContainerChange.set(domainObjectSet, ImmutableActionSet.empty<Any>())
        }catch (ignore:Exception) {
        }
        return ret
    }

是的,可以了,多谢:+1:

@trycatchx
Copy link
Owner

@manza7014 项目可以正常运行了是吧。那你按照这个代码提个 rq ,我合入。

@trycatchx trycatchx reopened this Dec 22, 2021
@trycatchx
Copy link
Owner

@manza7014 可以尝试一下修改 :AppProjectDependencies.kt 以下代码,我把修改依赖的时机提前一点。

 init {

        project.afterEvaluate {
            val allProject = mutableSetOf<Project>()
            project.rootProject.allprojects.forEach {
                allProject.add(it)
            }
//        project.gradle.projectsEvaluated {
//            // 调整依赖时机
//            if (isFirst) {
//                isFirst = false
//                resolveDenpendency()
//            }
//        }
            project.gradle.afterProject {
                allProject.remove(it)
                if (canbeResolveDenpendency(allProject)) {
                    // 调整依赖时机
                    if (isFirst) {
                        isFirst = false
                        resolveDenpendency()
                    }
                }
            }
        }
    }

    fun canbeResolveDenpendency(set: MutableSet<Project>): Boolean {
        var ret = true
        set.forEach {
            if (it != project.rootProject && it.childProjects.size <= 0
                    && (hasAndroidPlugin(it) || hasJavaPlugin(it))) {
                ret = false
                return@forEach
            }
        }
        return ret
    }

@manza7014
Copy link
Author

@manza7014 可以尝试一下修改 :AppProjectDependencies.kt 以下代码,我把修改依赖的时机提前一点。

 init {

        project.afterEvaluate {
            val allProject = mutableSetOf<Project>()
            project.rootProject.allprojects.forEach {
                allProject.add(it)
            }
//        project.gradle.projectsEvaluated {
//            // 调整依赖时机
//            if (isFirst) {
//                isFirst = false
//                resolveDenpendency()
//            }
//        }
            project.gradle.afterProject {
                allProject.remove(it)
                if (canbeResolveDenpendency(allProject)) {
                    // 调整依赖时机
                    if (isFirst) {
                        isFirst = false
                        resolveDenpendency()
                    }
                }
            }
        }
    }

    fun canbeResolveDenpendency(set: MutableSet<Project>): Boolean {
        var ret = true
        set.forEach {
            if (it != project.rootProject && it.childProjects.size <= 0
                    && (hasAndroidPlugin(it) || hasJavaPlugin(it))) {
                ret = false
                return@forEach
            }
        }
        return ret
    }
* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:compileTDCFDebugJavaWithJavac'.
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:207)
	at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:263)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:205)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:186)
	at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:114)
	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:62)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:409)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:399)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:242)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:150)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:94)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
	at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:41)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:356)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:322)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
Caused by: org.gradle.api.internal.tasks.compile.CompilationFailedException: Compilation failed; see the compiler error output for details

步骤:把昨天的修改: setBeforeContainerChangeToEmpty去掉了,只做了上面你说的这个修改然后报错

显示:

错误: 程序包com.yskj.applypermission不存在
import com.yskj.applypermission.PermissionChecker;

app module中某个文件引用的permission module中的类文件不存在,其他module文件也存在找不到的现象

@trycatchx
Copy link
Owner

使用 classpath 'io.github.trycatchx:rocketx:1.0.12' 解决以上问题

@manza7014
Copy link
Author

使用 classpath 'io.github.trycatchx:rocketx:1.0.12' 解决以上问题

好的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

2 participants