溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

11. Gradle編譯其他應(yīng)用代碼流程(一)

發(fā)布時(shí)間:2020-08-26 17:35:46 來源:網(wǎng)絡(luò) 閱讀:666 作者:rongwei84n 欄目:開發(fā)技術(shù)

由于我們可以把Gradle源代碼里面的launcher包丟到gradle libs下面去編譯應(yīng)用程序,所以我們可以用Gradle源代碼里面的launcher源代碼進(jìn)行分析。


至于Gradle源代碼和gradle bin的關(guān)系,可以參考上一篇帖子:

Gradle自身源代碼編譯


這個(gè)很重要,弄清楚雞和蛋的問題,下面正式開始。


一. GradleMain

文件路徑:

subprojects\launcher\src\main\java\org\gradle\launcher\GradleMain.java

public static void main(String[] args) throws Exception {
        new ProcessBootstrap().run("org.gradle.launcher.Main", args);
    }
    
    
public class ProcessBootstrap {
    /**
     * Sets up the ClassLoader structure for the given class, creates an instance and invokes {@link EntryPoint#run(String[])} on it.
     */
    public void run(String mainClassName, String[] args) {
        try {
            runNoExit(mainClassName, args);
            System.exit(0);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.exit(1);
        }
    }

    private void runNoExit(String mainClassName, String[] args) throws Exception {
        ClassPathRegistry classPathRegistry = new DefaultClassPathRegistry(new DefaultClassPathProvider(new DefaultModuleRegistry(CurrentGradleInstallation.get())));
        ClassLoaderFactory classLoaderFactory = new DefaultClassLoaderFactory();
        ClassPath antClasspath = classPathRegistry.getClassPath("ANT");
        ClassPath runtimeClasspath = classPathRegistry.getClassPath("GRADLE_RUNTIME");
        ClassLoader antClassLoader = classLoaderFactory.createIsolatedClassLoader(antClasspath);
        ClassLoader runtimeClassLoader = new VisitableURLClassLoader(antClassLoader, runtimeClasspath);

        ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(runtimeClassLoader);

        try {
            Class<?> mainClass = runtimeClassLoader.loadClass(mainClassName);
            Object entryPoint = mainClass.newInstance();
            Method mainMethod = mainClass.getMethod("run", String[].class);
            mainMethod.invoke(entryPoint, new Object[]{args});
        } finally {
            Thread.currentThread().setContextClassLoader(oldClassLoader);

            ClassLoaderUtils.tryClose(runtimeClassLoader);
            ClassLoaderUtils.tryClose(antClassLoader);
        }
    }
}


這行代碼其實(shí)就是啟動(dòng)org.gradle.launcher.main.run方法



二. org.gradle.launcher.main

其實(shí)走的是doAction方法,因?yàn)閞un方法是它的父類EntryPoint里面:

public abstract class EntryPoint {

    /**
     * Unless the createCompleter() method is overridden, the JVM will exit before returning from this method.
     */
    public void run(String[] args) {
        RecordingExecutionListener listener = new RecordingExecutionListener();
        try {
            doAction(args, listener);
        } catch (Throwable e) {
            createErrorHandler().execute(e);
            listener.onFailure(e);
        }

        Throwable failure = listener.getFailure();
        ExecutionCompleter completer = createCompleter();
        if (failure == null) {
            completer.complete();
        } else {
            completer.completeWithFailure(failure);
        }
    }



所以看doAction方法:

public class Main extends EntryPoint {
    public static void main(String[] args) {
        new Main().run(args);
    }

    protected void doAction(String[] args, ExecutionListener listener) {
        createActionFactory().convert(Arrays.asList(args)).execute(listener);
    }

    CommandLineActionFactory createActionFactory() {
        return new CommandLineActionFactory();
    }
}


//createActionFactory.java
public Action<ExecutionListener> convert(List<String> args) {
        ServiceRegistry loggingServices = createLoggingServices();

        LoggingConfiguration loggingConfiguration = new DefaultLoggingConfiguration();

        return new WithLogging(loggingServices,
                args,
                loggingConfiguration,
                new ExceptionReportingAction(
                        new JavaRuntimeValidationAction(
                                new ParseAndBuildAction(loggingServices, args)),
                        new BuildExceptionReporter(loggingServices.get(StyledTextOutputFactory.class), loggingConfiguration, clientMetaData())));
    }


從convert方法可以看到返回的是WithLogging對象,但是它里面又包了好幾層。


其實(shí)它們都是實(shí)現(xiàn)了Action<ExecutionListener>接口:

private static class WithLogging implements Action<ExecutionListener> {}

public class ExceptionReportingAction implements Action<ExecutionListener> {}

public class JavaRuntimeValidationAction implements Action<ExecutionListener> {}

private class ParseAndBuildAction implements Action<ExecutionListener> {}


這是裝飾者模式。


關(guān)于裝飾者模式大家可以百度下,大概有兩點(diǎn):

  1. 類實(shí)現(xiàn)同一個(gè)接口

  2. 互相包裝


打個(gè)比方,有這么一個(gè)場景:

咖啡店咖啡賣3塊一杯,于是我們可以這樣設(shè)計(jì):

interface Drink{
  int price();
}

class Coffee implements Drink{
   int price(){return 3;}
}


哪天需要開發(fā)了新產(chǎn)品,嗯,加糖的咖啡,需要賣5塊,那可以設(shè)計(jì)如下:

class SugarCoffee implements Drink{
    Drink mDrink;
    SugarCoffee(Drink drink){
        mDrink = drink;
    }
    
    int price(){
        return 2 + mDrink.price();
    }

}

main(){
    SugarCoffee sc = new SugarCoffee(new Coffee());
    int price = sc.price();
}


嗯,大概就是這個(gè)意思。。Gradle這里也用了同樣的模式。

最后,我們看最關(guān)鍵的ParseAndBuildAction.execute(xxx)方法



三. ParseAndBuildAction.execute(xxx)

protected void createActionFactories(ServiceRegistry loggingServices, Collection<CommandLineAction> actions) {
    actions.add(new GuiActionsFactory());
    actions.add(new BuildActionsFactory(loggingServices, new ParametersConverter(), new CachingJvmVersionDetector(new DefaultJvmVersionDetector(new DefaultExecActionFactory(new IdentityFileResolver())))));
}

public void execute(ExecutionListener executionListener) {
        	System.out.println("ParseAndBuildAction execute");
            List<CommandLineAction> actions = new ArrayList<CommandLineAction>();
            //添加BuiltInActions
            actions.add(new BuiltInActions());
            //添加GuiActionsFactory和BuildActionsFactory
            createActionFactories(loggingServices, actions);

            CommandLineParser parser = new CommandLineParser();
            for (CommandLineAction action : actions) {
                action.configureCommandLineParser(parser);
            }

            Action<? super ExecutionListener> action;
            try {
                ParsedCommandLine commandLine = parser.parse(args);
                action = createAction(actions, parser, commandLine);
                
                Exception exx = new Exception("Sandt ParseAndBuildAction execute..");
                exx.printStackTrace();
                
                System.out.println("action: " + action);
            } catch (CommandLineArgumentException e) {
                action = new CommandLineParseFailureAction(parser, e);
            }

            action.execute(executionListener);
        }
        

private Action<? super ExecutionListener> createAction(Iterable<CommandLineAction> factories, CommandLineParser parser, ParsedCommandLine commandLine) {
      for (CommandLineAction factory : factories) {
           Runnable action = factory.createAction(parser, commandLine);
           if (action != null) {
               return Actions.toAction(action);
           }
      }
      throw new UnsupportedOperationException("No action factory for specified command-line arguments.");
}


  1. execute里面首先會(huì)添加3個(gè)ActionFactory

    BuildInActions表示gradle -help和gradle -version 命令

    GuiActionsFactory表示gradle -gui命令

    BuildActionsFactory表示其他編譯命令,比如gradle assemble,下面我們主要分析這種。


  2. 調(diào)用createAction方法,其實(shí)就是挨個(gè)調(diào)用上面3個(gè)ActionsFacory的createAction方法。那我們主要看BuildActionsFactory。

       

  

四. BuildActionsFactory.createAction

public Runnable createAction(CommandLineParser parser, ParsedCommandLine commandLine) {
        Parameters parameters = parametersConverter.convert(commandLine, new Parameters());
        parameters.getDaemonParameters().applyDefaultsFor(jvmVersionDetector.getJavaVersion(parameters.getDaemonParameters().getEffectiveJvm()));

        if (parameters.getDaemonParameters().isStop()) {
        	System.out.println("createAction 1");
            return stopAllDaemons(parameters.getDaemonParameters(), loggingServices);
        }
        if (parameters.getDaemonParameters().isStatus()) {
        	System.out.println("createAction 2");
            return showDaemonStatus(parameters.getDaemonParameters(), loggingServices);
        }
        if (parameters.getDaemonParameters().isForeground()) {
        	System.out.println("createAction 3");
            DaemonParameters daemonParameters = parameters.getDaemonParameters();
            ForegroundDaemonConfiguration conf = new ForegroundDaemonConfiguration(
                UUID.randomUUID().toString(), daemonParameters.getBaseDir(), daemonParameters.getIdleTimeout(), daemonParameters.getPeriodicCheckInterval());
            return new ForegroundDaemonAction(loggingServices, conf);
        }
        if (parameters.getDaemonParameters().isEnabled()) {
        	System.out.println("createAction 4");
            return runBuildWithDaemon(parameters.getStartParameter(), parameters.getDaemonParameters(), loggingServices);
            
//        	return runBuildInProcess(parameters.getStartParameter(), parameters.getDaemonParameters(), loggingServices);
        }
        if (canUseCurrentProcess(parameters.getDaemonParameters())) {
        	System.out.println("createAction 5");
            return runBuildInProcess(parameters.getStartParameter(), parameters.getDaemonParameters(), loggingServices);
        }

        System.out.println("createAction 6");
        return runBuildInSingleUseDaemon(parameters.getStartParameter(), parameters.getDaemonParameters(), loggingServices);
    }


編譯種類主要分為兩種:

  1. 在本進(jìn)程編譯

  2. 在守護(hù)進(jìn)程編譯

選擇邏輯是:

  1. 如果有守護(hù)進(jìn)程在運(yùn)行,那么連接守護(hù)進(jìn)程然后在守護(hù)進(jìn)程編譯。

  2. 如果可以在當(dāng)前進(jìn)程編譯,那么就在當(dāng)前進(jìn)程編譯。

  3. 如果都不滿足,那就啟動(dòng)守護(hù)進(jìn)程,然后進(jìn)行編譯。


判斷是否可以在當(dāng)前進(jìn)程編譯的代碼如下:

subprojects\launcher\src\main\java\org\gradle\launcher\daemon\configuration\BuildProcess.java


/**
     * Attempts to configure the current process to run with the required build parameters.
     * @return True if the current process could be configured, false otherwise.
     */
    public boolean configureForBuild(DaemonParameters requiredBuildParameters) {
        boolean javaHomeMatch = getJvm().equals(requiredBuildParameters.getEffectiveJvm());

        final JvmOptions jvmOptions = new JvmOptions(new IdentityFileResolver());
        jvmOptions.systemProperties(getJvmOptions().getImmutableSystemProperties());
        List<String> currentImmutables = jvmOptions.getAllImmutableJvmArgs();
        List<String> requiredImmutables = requiredBuildParameters.getEffectiveSingleUseJvmArgs();
        requiredImmutables.removeAll(DaemonParameters.DEFAULT_JVM_ARGS);
        

        boolean noImmutableJvmArgsRequired = requiredImmutables.equals(currentImmutables);
        
        System.out.println("javaHomeMatch: " + javaHomeMatch 
        		+ " noImmutableJvmArgsRequired: " + noImmutableJvmArgsRequired);
        
        System.out.println("getJvm(): " + getJvm()
        	+ " requiredBuildParameters.getEffectiveJvm(): " + requiredBuildParameters.getEffectiveJvm());
        
        for (String re : requiredImmutables) {
			System.out.println("requiredImmutable: " + re);
		}
        for (String re : currentImmutables) {
			System.out.println("currentImmutable: " + re);
		}
        
        if (javaHomeMatch && noImmutableJvmArgsRequired) {
            // Set the system properties and use this process
            Properties properties = new Properties();
            properties.putAll(requiredBuildParameters.getEffectiveSystemProperties());
            System.setProperties(properties);
            return true;
        }
        return false;
    }


打印日志:

javaHomeMatch: true noImmutableJvmArgsRequired: false
getJvm(): 1.8.0_25 (Oracle Corporation 25.25-b02) requiredBuildParameters.getEffectiveJvm(): 1.8.0_25 (Oracle Corporation 25.25-b02)
requiredImmutable: -Xmx1536m
requiredImmutable: -Dfile.encoding=GBK
requiredImmutable: -Duser.country=CN
requiredImmutable: -Duser.language=zh
requiredImmutable: -Duser.variant
currentImmutable: -Dfile.encoding=GBK
currentImmutable: -Duser.country=CN
currentImmutable: -Duser.language=zh
currentImmutable: -Duser.variant


從日志看環(huán)境有點(diǎn)不一樣,所以我的環(huán)境會(huì)采用守護(hù)進(jìn)程的方法。 不過為了調(diào)試,我可以修改代碼強(qiáng)制采用本進(jìn)程編譯的方式。


這兩種編譯流程不盡相同,下一篇文章會(huì)先講在本進(jìn)程編譯的流程。然后再講啟動(dòng)守護(hù)進(jìn)程進(jìn)程編譯流程。





向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI