Giter VIP home page Giter VIP logo

seal's Introduction

Seal

Maven Central Actions Status Apache 2

English | 中文说明

Seal is a Gradle Plugin to resolve AndroidManifest.xml merge conflicts, powered by New Variant/Artifact API & Polyfill.

To be noticed, except the tag removing, any other delete/update features should always consider the "tools:replace", "tools:remove", and other official features that ManifestMerger provided as higher priority.

Functionality that Seal provided is more like the first aid to save an urgent publish that is blocked by ManifestMerger, including pre/post processors to intercept the merge flow of AndroidManifest.xml. Developers should take responsibility to report bugs to library authors(who introduced problematic Manifest), ManifestMerger(Google), AAPT2(Google), which is the true way to solve the merge issues.

Quick Start

0x01. Add the plugin to classpath:

// Option 1.
// Add `mavenCentral` to `pluginManagement{}` on `settings.gradle.kts` (or the root `build.gradle.kts`),
// and then the seal plugin id.
pluginManagement {
	repositories {
        ...
        mavenCentral()
    }
    plugins {
    	...
    	id("me.2bab.seal") version "3.4.0" apply false
    }
}

// Option 2.
// Using classic `buildscript{}` block in root build.gradle.kts.
buildscript {
    repositories {
        ...
        mavenCentral()
    }
    dependencies {
        classpath("com.android.tools.build:gradle:8.1.2")
        classpath("me.2bab:seal:3.4.0")
    }
}

0x02. Apply the plugin:

// On Application's build.gradle.kts (do not use in Library project)
plugins {
    id("com.android.application")
    kotlin("android")
    // Apply this plugin
    id("me.2bab.seal")
}

0x03. Configurations

seal {

    // 0. Two cases for before merge.
    beforeMerge("Remove description attr for library input Manifest.")
        .tag("application")
        .attr("android:description")
        .deleteAttr()
    beforeMerge("Remove problematic replace attr for library input Manifest.")
        .tag("application")
        .attr("tools:replace")
        .deleteAttr()

    // Full covered cases for after merge (1-5).
    // 1. The target of this operation is too broad, please specify the attr and value if possible.
    afterMerge("Remove all uses-feature tags.")
        .tag("uses-feature")
        .deleteTag()

    // 2. The target of this operation is too broad, please specify the value if possible.
    afterMerge("Remove all custom permission tags.")
        .tag("permission")
        .attr("android:protectionLevel")
        .deleteTag()

    // 3. This is the way we recommend to delete the tag(s).
    afterMerge("Remove invalid service tag.")
        .tag("service")
        .attr("android:name")
        .value("me.xx2bab.seal.sample.library.LegacyService")
        .deleteTag()

    // You should try to use "tools:remove" or "tools:replace" instead of "deleteAttr" if possible
    // 4. To delete an attr and its value.
    afterMerge("Remove application's allowBackup attr.")
        .tag("application")
        .attr("android:allowBackup")
        .deleteAttr()

    // You should try to use "tools:remove" or "tools:replace" instead of "deleteAttr" if possible
    // 5. Also u can specify the value as part of finding params.
//    afterMerge("Remove application's allowBackup attr.")
//        .tag("application")
//        .attr("android:allowBackup")
//        .value("true")
//        .deleteAttr()

}

The configuration is separated by 3 parts:

  1. To specify the hook entry which you can select from beforeMerge(ruleName: String) or afterMerge(ruleName: String), before intercepts all merge inputs (all libraries, except the main application one), while after modifies the merged AndroidManifest.xml;
  2. To specify the search params which you can pass tag(name: String) attr(name: String) value(name: String) (currently we haven't support regex), please pass as precise as you can to locate the element
  3. To specify the delete type which you an select from deleteTag() or deleteAttr(), to be noticed, only one delete action will be executed, DO NOT call more than one deleteXXX

Common issues:

  1. Warning: AndroidManifest.xml already defines debuggable (in http://schemas.android.com/apk/res/android); using existing value in manifest.

That's because some out-of-date libraries set debuggable at AndroidManifest, but now we pass this setting from build.gradle / build.gradle.kts to AAPT.

  1. Multiple entries with same key: @android:theme=REPLACE and android:theme=REPLACE / Multiple entries with same key: @android:allowBackup=REPLACE and android:allowBackup=REPLACE.

There is a library which defined android:allowBackup=true conflicts with yours (android:allowBackup=false). You wanna to override it using tools:replace="android:allowBackup", but find that tools:replace="android:allowBackup" is also present at lib's manifest, finally the conflict shows above. (Also see this)

  1. Sometimes xmlns is wrote in application or any other tags except manifest tag, may cause aapt's concealed defect,like debuggable setting of build.gradle would not work;

Please check this link for more info.

  1. Error: tools:replace specified at line:25 for attribute android:authorities, but no new value specified

Please check this link for more info.

Compatible Specification

Polyfill is only supported & tested on latest 2 Minor versions of Android Gradle Plugin. Since 3.0.2, the publish repository has been shifted to Maven Central.

AGP Version Latest Support Version
8.1.x / 8.0.x Maven Central
7.2.x 7.1.x 3.3.0
7.0.x 3.1.0
4.2.x 3.0.2
3.0.x 2.0.0
2.3.x 1.1.0

Why Seal use DOM parser API

Oracle Docs: Comparing StAX to Other JAXP APIs

Since we need to support "delete tag" feature, and export outputs simply, from the link above we can know DOM is easiest one to process that. Though it consumes more CPU and memory resources, luckily most of AndroidManifest.xml are not complex and with the help of Gradle we can cache the task result if those input(s) didn't change.

License

Copyright Since 2017 2BAB

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.

seal's People

Contributors

2bab avatar ljl233 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

seal's Issues

add a sample project

I suggest to have these 2 modules:

  • /app: which applies "com.android.application", contains the base Manifest
  • /lib: which applies "com.android.library", provides its own AndroidManifest.xml that will be merged to the base, we can add a batch of cases into it.

Compile Failed After use seal plugin

version info:
gradle: 7.0.2
agp: 7.0.0
seal: 3.1.0

seal config


seal {

//     0. Two cases for before merge.
    beforeMerge("Remove problematic replace attr for library input Manifest.")
            .tag("application")
            .attr("tools:replace")
            .deleteAttr()


    beforeMerge("Remove problematic replace attr for library input Manifest.")
            .tag("application")
            .attr("tools:remove")
            .deleteAttr()
}

full error log

A problem was found with the configuration of task ':app:processDebugManifest' (type 'ProcessMultiApkApplicationManifest').
  - Type 'com.android.build.gradle.tasks.ProcessMultiApkApplicationManifest' property 'mainMergedManifest' specifies file '/Users/xxrl/codebase/project/application/app/build/intermediates/merged_manifest/debug/postUpdateDebugManifest/AndroidManifest.xml' which doesn't exist.

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.internal.execution.WorkValidationException: A problem was found with the configuration of task ':app:processDebugManifest' (type 'ProcessMultiApkApplicationManifest').
  - Type 'com.android.build.gradle.tasks.ProcessMultiApkApplicationManifest' property 'mainMergedManifest' specifies file '/Users/xxrl/codebase/project/application/app/build/intermediates/merged_manifest/debug/postUpdateDebugManifest/AndroidManifest.xml' which doesn't exist.
    
    Reason: An input file was expected to be present but it doesn't exist.
    
    Possible solutions:
      1. Make sure the file exists before the task is called.
      2. Make sure that the task which produces the file is declared as an input.
    
    Please refer to https://docs.gradle.org/7.0.2/userguide/validation_problems.html#input_file_does_not_exist for more details about this problem.
	at org.gradle.internal.execution.WorkValidationException$BuilderWithSummary.build(WorkValidationException.java:109)
	at org.gradle.internal.execution.WorkValidationException$BuilderWithSummary.get(WorkValidationException.java:91)
	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:97)
	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:50)
	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.lambda$execute$2(SkipEmptyWorkStep.java:86)
	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:86)
	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:32)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
	at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:43)
	at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:31)
	at org.gradle.internal.execution.steps.AssignWorkspaceStep.lambda$execute$0(AssignWorkspaceStep.java:40)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution$2.withWorkspace(ExecuteActionsTaskExecuter.java:283)
	at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:40)
	at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:30)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:37)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:27)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:49)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:35)
	at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:76)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:184)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:173)
	at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:109)
	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
	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.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76)
	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
	at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:408)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:395)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:388)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:374)
	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)

老哥,请问这个具体咋用😂

我看中文文档介绍在根项目的build.gradle里配置classpath,但是现在新的AndroidStudio里已经没classpath了,不知道该咋配😂

dependencies {
        classpath("com.android.tools.build:gradle:7.1.2")
        classpath("me.2bab:seal:3.2.0")
    }
plugins {
    id 'com.android.application' version '7.2.1' apply false
    id 'com.android.library' version '7.2.1' apply false
    id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

还有plugin的id是配置在这里面的吗?是跟着上面的一起写吗?
还有seal的配置是写在哪的,我都不知道咋写😥

Could not find me.2bab:seal:2.0.0.

repositories {
google()
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'me.2bab:seal:2.0.0'
}

====================================================

Could not find me.2bab:seal:2.0.0.
Searched in the following locations:

Error:Could not find method install() for arguments [bintray_6rcjx07b0cwg3oytb1rkljhd5$_run_closure1@211f2539] on project ':blsdk' of type org.gradle.api.Project.

Error:Could not find method install() for arguments [bintray_6rcjx07b0cwg3oytb1rkljhd5$_run_closure1@211f2539] on project ':blsdk' of type org.gradle.api.Project.

我用了你的插件 使用方式是
apply from: 'bintray.gradle'
buildscript {
repositories {
jcenter()
mavenCentral()
}

dependencies {
    classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
    classpath 'com.github.dcendents:android-maven-plugin:1.2'
}

}

allprojects {
repositories {
jcenter()
mavenCentral()
}
}

uploadArchives {
repositories {
mavenDeployer {
repository(url: uri(System.getProperty("user.home") + '/.m2/repository'))
}
}
}
//相关库依赖
def projectRoot = project.getRootProject().rootDir.absolutePath
// 依赖库的 Manifest 文件搜索路径
// 1. Gradle plugin 2.3.0 或者更高版本,会默认开启 build-cache 功能,Release 版本的库会解压到这里
// 2. 但是我们同样需要对 SNAPSHOT 的库做预检查,所以还需要加入 exploded-aar 的目录
// 3. 有更多自定义的目录或者 module,请自行添加
def manifestPath = [
// for AAR of Release
// see note below
projectRoot + '/build-cache',
// for AAR of SNAPSHOT
projectRoot + '/app/build/intermediates/exploded-aar'
]
def removeAttrs = [
'android:debuggable'
]
def replaceValues = [
'android:allowBackup'
]
seal {
enabled = true
manifests = manifestPath
appAttrs {
enabled = true
attrsShouldRemove = removeAttrs
}
appReplaceValues {
enabled = true
valuesShouldRemove = replaceValues
}
}
在我需要打包的model 中 但是出现了这个问题 不知道为什么 是因为这个必须在application 中使用么

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.