2017-01-16 1 views
0

저는 Android Studio 및 Android 프로젝트 개발에 익숙합니다. 안드로이드 스튜디오에서 IntelliJ를 사용하여 jar 파일 이름에 버전 정보 추가

나는 build.gradle 파일에 넣고 :

defaultConfig { 
    applicationId "com.domain.myapp" 
    minSdkVersion 19 
    targetSdkVersion 19 
    versionCode 1 
    versionName "1.0" 
    setProperty("archivesBaseName", "myapp.$versionName.$versionCode") 
    signingConfig signingConfigs.config 
} 
buildTypes { 
    release { 
     minifyEnabled false 
     proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
     applicationVariants.all { variant -> 
      variant.outputs.each { output -> 
       def newName = output.outputFile.name 
       newName = newName.replace("-release", "") 
       output.outputFile = new File(output.outputFile.parent, newName) 
      } 
     } 
     signingConfig signingConfigs.config 
    } 
    debug { 
     signingConfig signingConfigs.config 
    } 
} 

내가 myapp.1.0.1.apk 얻을 구축, 훌륭하게 작동합니다.

이제 Android Studio가 아닌 IntelliJ를 사용하여 Java 프로젝트 .jar을 개발 중입니다.

어떻게하면 동일한 작업을 수행 할 수 있습니까? 나는 희소 한 정보를 찾고 있습니다 ...

답변

0

안드로이드는 R.java 파일을 작성하는 작업을 추가하여이 작업을 수행합니다. 안드로이드가 동작을 복제하는 것처럼 보이게하는 것만 큼 약간의 노력이 필요합니다. 동일한 목표를 달성하기 위해 자체 grad 작업을 만들 수 있습니다. 생성 및 확장과 작업을하는 gradle 플러그인이 필요합니다. 확장은

다음
// create DSL model 
    target.extensions.create('buildConfig', BuildConfigModel) 
    // create task to generate file 
    def buildConfigTask = target.tasks.create('generateBuildConfig', BuildConfigTask) 
    target.tasks.findByName('clean').configure { 
     delete new File("$project.projectDir/src/main", "generated-sources") 
    } 
    // this task must always run... it's never `upToDate` 
    buildConfigTask.outputs.upToDateWhen { false } 
    // java compiler depends on this task... allows us to reference generated code from 
    // human written code 
    target.tasks.getByName('compileJava').dependsOn buildConfigTask 

당신이 생성 된 소스 파일을 추가 할 작업을 사용할 수있는 방법입니다 확장을 만들어야합니다 build.gradle

플러그인에 추가 된 값과 작업을 추적하는 데 사용됩니다

project.configure(project, { Project configuredProject -> 
     // compilerJava task {@link JavaCompile} 
     def compileJava = configuredProject.compileJava 
     // add the created java file to source path so it gets compiled 
     compileJava.source(project.buildConfig.createBuildConfig(project, compileJava)) 
    }) 

그러면 확장 프로그램은 다음과 같이 보입니다.

package com.jbirdvegas.q41680813; 

import com.squareup.javapoet.CodeBlock; 
import com.squareup.javapoet.FieldSpec; 
import com.squareup.javapoet.JavaFile; 
import com.squareup.javapoet.MethodSpec; 
import com.squareup.javapoet.TypeSpec; 
import org.gradle.api.GradleException; 
import org.gradle.api.Project; 
import org.gradle.api.tasks.compile.JavaCompile; 

import javax.lang.model.element.Modifier; 
import java.io.File; 
import java.io.FileWriter; 
import java.io.IOException; 
import java.util.ArrayList; 
import java.util.Collections; 
import java.util.List; 
import java.util.Map; 
import java.util.stream.Collectors; 

/** 
* Handles creating the BuildConfig.java from a module project 
* <p> 
* Warning... Keep this in java, gradle doesn't have the streams api we are using 
*/ 
public class BuildConfigModel { 
    /** 
    * Final java file output path pattern for {@link String#format(String, Object...) String#format} formatting of 
    * the file's path. Directory structure will be created if needed 
    */ 
    private static final String OUTPUT_PATH_FORMAT = "%s/src/main/generated-sources/%s/%s/BuildConfig.java"; 

    /** 
    * List of DSL supplied {@link BuildValue buildConfig#add} 
    */ 
    private List<BuildValue> mBuildValues = new ArrayList<>(); 

    /** 
    * Required... do not remove 
    */ 
    public BuildConfigModel() { 
    } 

    /** 
    * Create a new field to the project's generated `BuildConfig.java`'s inner class for each type 
    * 
    * @param clazz Type of value to be written (will be grouped by clazz) 
    * @param name field name to be created 
    * @param value value to be assigned to field's name 
    */ 
    @SuppressWarnings({"unused", "WeakerAccess"}) 
    public void add(Class clazz, String name, Object value) { 
     mBuildValues.add(new BuildValue(clazz, name, value)); 
    } 

    /** 
    * Create `BuildConfig.java` and add it to the {@link JavaCompile#source(Object...)} compileJava#source(Object...)} 
    * 
    * @param project  module to generate BuildConfig for 
    * @param javaCompile project's `compileJava` task 
    * @return generated `BuildConfig.java` file 
    */ 
    public File createBuildConfig(Project project, JavaCompile javaCompile) { 
     File buildConfigFile = getBuildConfigFile(project); 
     createJavaClass(project, buildConfigFile); 
     javaCompile.source(buildConfigFile); 
     return buildConfigFile; 
    } 

    /** 
    * Destination file for given project's `BuildConfig.java` 
    * 
    * @param project module to generate BuildConfig for 
    * @return File representing the destination of the created `BuildConfig.java` 
    */ 
    @SuppressWarnings("WeakerAccess") 
    public File getBuildConfigFile(Project project) { 
     return project.file(String.format(OUTPUT_PATH_FORMAT, 
       project.getProjectDir().getAbsolutePath(), 
       project.getGroup().toString().replaceAll("\\.", "/"), 
       project.getName())); 
    } 

    /** 
    * Create `BuildConfig.java` with a few default values and any values supplied 
    * to the `buildConfig`'s {@link #add(Class, String, Object) add} method. 
    * <p> 
    * Default BuildConfig fields will be generated by {@link #getDefaultFields} 
    * <p> 
    * Fields added via {@link #add(Class, String, Object) add} method will be grouped into inner classes 
    * named <pre>{@code Class#getSimpleName().toLowerCase() + "s"}</pre> 
    * 
    * @param project   module to generate BuildConfig for 
    * @param buildConfigFile File representing the destination of the BuildConfig.java output 
    */ 
    @SuppressWarnings("WeakerAccess") 
    public void createJavaClass(Project project, File buildConfigFile) { 
     //noinspection unchecked 
     Collections.sort(mBuildValues); 
     // group our configs by their types into a map of groups 
     Map<Class, List<BuildValue>> groupedConfigs = mBuildValues.stream() 
       // put the values in groups based on the simple name of the class in lowercase 
       .collect(Collectors.groupingBy(BuildValue::getValueType)); 

     // create the fully qualified class that will contain our build settings 
     TypeSpec.Builder buildConfigJavaBuilder = TypeSpec.classBuilder("BuildConfig") 
       .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 
       // note for javadoc 
       .addJavadoc("$S\n", "DO NOT MODIFY; this class is written automatically by the compiler") 
       // replace public constructor with private 
       .addMethod(createPrivateConstructor()); 

     // add any fields that will be in all BuildConfig classes 
     buildConfigJavaBuilder.addFields(getDefaultFields(project)); 

     groupedConfigs.forEach((aClass, buildValues) -> { 
      // create the inner class 
      String safeInnerClassName = aClass.getSimpleName().toLowerCase() + 's'; 
      TypeSpec.Builder innerClass = TypeSpec.classBuilder(safeInnerClassName) 
        .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) 
        // make a constructor that's private since all the members of this class are public static final 
        .addMethod(createPrivateConstructor()); 
      // for each inner class type create a field 
      // each object type gets it's own inner class 
      //noinspection SimplifyStreamApiCallChains 
      buildValues.stream().forEachOrdered(buildValue -> { 
       // create the requested field in the class 
       FieldSpec fieldSpec = FieldSpec.builder(buildValue.clazz, buildValue.name) 
         .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) 
         .initializer(CodeBlock.of(getStringFormatter(buildValue.clazz), buildValue.value)) 
         .build(); 
       // add the field to the inner class 
       innerClass.addField(fieldSpec); 
      }); 
      // add the inner class to the fully qualified class 
      buildConfigJavaBuilder.addType(innerClass.build()); 
     }); 

     // determine the package name from project.group + '.' + project.name 
     String packageName = project.getGroup() + "." + project.getName(); 
     // create a java file writer 
     JavaFile.Builder builder = JavaFile.builder(packageName, buildConfigJavaBuilder.build()); 
     // don't import java.lang.* it's redundant 
     builder.skipJavaLangImports(true); 
     // use four spaces for indent instead of default two spaces 
     builder.indent(" "); 
     // create the file in memory 
     JavaFile javaFile = builder.build(); 

     // ensure file structure 
     if (!buildConfigFile.getParentFile().exists() && !buildConfigFile.getParentFile().mkdirs()) { 
      throw new GradleException("Failed to create directory structure for " + buildConfigFile.getAbsolutePath()); 
     } 

     // write BuildConfig.java to location 
     try (FileWriter writer = new FileWriter(buildConfigFile)) { 
      javaFile.writeTo(writer); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    /** 
    * Strings require being treated specially in order to be encapsulated in quotes correctly 
    * All other classes are treated as literals... We may want to handle more {@link java.lang.reflect.Type Type} 
    * 
    * @param clazz Class formatter is needed for 
    * @return "$S" if the class is a {@link String} else a literal formatter is returned "$L" 
    */ 
    private String getStringFormatter(Class clazz) { 
     switch (clazz.getSimpleName().toLowerCase()) { 
      case "string": 
       // causes the formatter used to wrap the value in quotes correctly 
      case "date": 
       // date objects are serialized to a string 
       return "$S"; 
      case "long": 
       return "$LL"; 
      case "double": 
       return "$LD"; 
      case "float": 
       return "$LF"; 
      default: 
       // for the reset use literal 
       return "$L"; 
     } 
    } 

    /** 
    * get project added build values 
    * 
    * @return List of build values added by project's `buildConfig` closure 
    */ 
    @SuppressWarnings("unused") 
    public List<BuildValue> collectBuildValues() { 
     return mBuildValues; 
    } 

    /** 
    * Make a private constructor for the class. Default is public but our classes only contain 
    * <pre>{@code public static final {@link Object}}</pre> so public constructors are redundant 
    * 
    * @return private constructor method 
    */ 
    private MethodSpec createPrivateConstructor() { 
     return MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build(); 
    } 

    /** 
    * Create default field references 
    * 
    * @param project module to generate BuildConfig for 
    * @return List of fields to write to generated BuildConfig 
    */ 
    private List<FieldSpec> getDefaultFields(Project project) { 
     List<FieldSpec> fields = new ArrayList<>(); 

     // set current version 
     fields.add(FieldSpec.builder(String.class, "version") 
       .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) 
       .initializer(CodeBlock.of(getStringFormatter(String.class), project.getVersion())) 
       .build()); 
     return fields; 
    } 

    class BuildValue implements Comparable { 
     /** 
     * Type of field's value 
     */ 
    /* package */ Class clazz; 

     /** 
     * Field name 
     */ 
    /* package */ String name; 

     /** 
     * Field's value Value must be able to be serialized as a string 
     */ 
    /* package */ Object value; 

     /* package */ BuildValue(Class clazz, String name, Object value) { 
      this.clazz = clazz; 
      this.name = name; 
      this.value = value; 
     } 

     /* package */ Class getValueType() { 
      return clazz; 
     } 

     @Override 
     public boolean equals(Object o) { 
      if (this == o) return true; 
      if (!(o instanceof BuildValue)) return false; 

      BuildValue that = (BuildValue) o; 

      if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null) return false; 
      if (name != null ? !name.equals(that.name) : that.name != null) return false; 
      return value != null ? value.equals(that.value) : that.value == null; 
     } 

     @Override 
     public int hashCode() { 
      int result = clazz != null ? clazz.hashCode() : 0; 
      result = 31 * result + (name != null ? name.hashCode() : 0); 
      result = 31 * result + (value != null ? value.hashCode() : 0); 
      return result; 
     } 

     @Override 
     public int compareTo(Object o) { 
      return (name != null && o != null) ? name.compareTo(o.toString()) : -1; 
     } 

     @Override 
     public String toString() { 
      final StringBuilder sb = new StringBuilder("BuildValue{"); 
      sb.append("class=").append(clazz.getCanonicalName()); 
      sb.append(", name='").append(name).append('\''); 
      sb.append(", value=").append(value); 
      sb.append('}'); 
      return sb.toString(); 
     } 
    } 
} 

여기에 기본적으로 플러그인은 version의 기본 필드와 BuildConfig.java을 만들하지만 당신은 당신이 BuildConfig.value과 참조의 값을 얻을 수 또한 다음

buildConfig { 
    add Boolean, 'myKey', false 
} 

자신의 값을 런타임에 추가 할 수 있고 예를 들어 위의 당신은 또한 것 Boolean 유형으로 사용할 수있는 BuildConfig.myKey 필드가 있어야합니다.

편집 : 내 예제에서는 플러그인 클래스와 작업 클래스에 groovy를 사용하지만 확장자는 BuildConfigModel이며 Java로 작성되었습니다. 내 출처는 모두 src/main/groovy

+0

와우에 있습니다. 자세한 답변 해 주셔서 감사합니다. 오늘은이 문제를 해결할 수 없지만 곧 구현할 것입니다. 거의 수동으로 버전 정보를 직접 설정하는 것이 더 쉬워 보이지만 내 프로젝트 목록이 커지면서 결국에는 자동화 된 솔루션을 설치하는 것이 가치가 있습니다. –

관련 문제