Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/tfs/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Xcode 最快的j2objc集成_Xcode_J2objc - Fatal编程技术网

Xcode 最快的j2objc集成

Xcode 最快的j2objc集成,xcode,j2objc,Xcode,J2objc,我只想和你们分享我们的j2objc设置,因为我们花了一些时间在我们的一个项目中对其进行微调。我们目前正在使用j2objc编译400多个文件,在包中有重复的类名,必须能够在xcode中调试java代码,以便我们可以直接放置断点和分步执行java代码(而不是objc)。上一次我们为改进构建时间所做的调整为我们节省了50%的CI构建时间,与最初(官方建议的)方法相比,这是一个很大的帮助。我们正在一个单独的框架中构建j2objc部分,该框架是我们使用的java库的一部分。要点是使用不同的方法将java转

我只想和你们分享我们的j2objc设置,因为我们花了一些时间在我们的一个项目中对其进行微调。我们目前正在使用j2objc编译400多个文件,在包中有重复的类名,必须能够在xcode中调试java代码,以便我们可以直接放置断点和分步执行java代码(而不是objc)。上一次我们为改进构建时间所做的调整为我们节省了50%的CI构建时间,与最初(官方建议的)方法相比,这是一个很大的帮助。

我们正在一个单独的框架中构建j2objc部分,该框架是我们使用的java库的一部分。要点是使用不同的方法将java转换为objC,这不是使用构建规则,而是“运行脚本”构建阶段。请注意,我们确实需要能够在xcode内部调试java代码,因此j2objc团队建议的外部目标方法对我们不起作用,而构建规则方法确实在拖我们的后腿

我们在编译源代码阶段之前添加了构建阶段:

"${PROJECT_DIR}/scripts/j2objc.sh" "${PROJECT_DIR}" "${J2OBJC_HOME}" "${JAVA_SOURCE}";
j2objc.sh:

#!/bin/sh

if [ -z ${1} ]; then
    echo "error: PROJECT_DIR is not set."
    exit 1;
fi;

if [ -z ${2} ]; then
    echo "error: J2OBJC_HOME is not set."
    exit 1;
fi;

if [ -z ${3} ]; then
    echo "error: JAVA_SOURCE is not set."
    exit 1;
fi; 

PROJECT_DIR=$1
J2OBJC_HOME=$2
JAVA_SOURCE=$3


if [ ! -f "${J2OBJC_HOME}/j2objc" ]; then
    echo "J2OBJC_HOME is not correctly defined, currently set to '${J2OBJC_HOME}'";
    exit 1;
fi;

SHOULD_COMPILE=$(find "${JAVA_SOURCE}" -name '*.java' | {
        while read filename; do 
            JAVA_PATH="${filename}";
            JAVA_RELATIVE_PATH=$(sed -e "s|^$JAVA_SOURCE||" <<< "${JAVA_PATH}");
            BASE_RELATIVE_PATH=$(sed -e "s|.java$||" <<< "${JAVA_RELATIVE_PATH}");
            BASE_ABSOLUTE_PATH="${JAVA_SOURCE}/${BASE_RELATIVE_PATH}";
            H_PATH="${BASE_ABSOLUTE_PATH}.h";
            M_PATH="${BASE_ABSOLUTE_PATH}.m";

            if [ ! -f "${H_PATH}" ] || [ ! -f "${M_PATH}" ] || [ "${H_PATH}" -ot "${JAVA_PATH}" ] || [ "${M_PATH}" -ot "${JAVA_PATH}" ]; then
                echo "1";
                break;
            fi;
        done
    }
)

if [ "$SHOULD_COMPILE" = "1" ]; then
    "${J2OBJC_HOME}/j2objc" \
        -d "${JAVA_SOURCE}" \
        -sourcepath "${JAVA_SOURCE}" \
        -classpath "${J2OBJC_HOME}/lib/jsr305-3.0.0.jar" \
        --swift-friendly \
        --strip-reflection \
        --no-segmented-headers \
        -use-arc \
        --static-accessor-methods \
        --nullability \
        --prefixes "${PROJECT_DIR}/prefixes.properties" \
        -g \
        `find "${JAVA_SOURCE}" -name '*.java'`;
fi;
使用这种方法实际实现的是更快的j2objc翻译,因为您没有为每个要翻译的文件执行单独的j2objc实例,而是告诉一个j2objc实例一次翻译所有java文件,最后,您只需使用构建规则告诉xcode翻译文件的位置,以便对其进行编译。您应该只将java文件添加到项目中,而不是objC文件,它们将根据构建规则进行编译

我们使用脚本自动生成objC文件到框架中的导入,它也在构建阶段定义,并且必须在编译源代码构建阶段之后:

#!/bin/sh

PROJ_HEADER="${PROJECT_DIR}/Proj.h";
PROJ_TMP_HEADER="${PROJECT_DIR}/Proj.h.tmp";
HEADERS_PATH="${PROJECT_DIR}/../src/main/java/";

cat /dev/null > "${PROJ_TMP_HEADER}";
echo "// Generated by Proj external build target

#ifndef proj_h
#define proj_h

#import \"Proj/Bridge.h\"
" >> "${PROJ_TMP_HEADER}";
find "${PROJECT_DIR}/../src/main/java" -type f -name '*.h' | sed -e "s|^$HEADERS_PATH||" | sort | awk '{print "#import \"" $0 "\""}' >> "${PROJ_TMP_HEADER}";
echo "

#endif" >> "${PROJ_TMP_HEADER}";

FILE1=`cat "${PROJ_HEADER}" 2>/dev/null`;
FILE2=`cat "${PROJ_TMP_HEADER}"`;

if [ "$FILE1" = "$FILE2" ]; then
    rm -f "${PROJ_TMP_HEADER}";
else
    mv "${PROJ_TMP_HEADER}" "${PROJ_HEADER}";
fi;
最后的逻辑是这样的,我们不会在每个构建中更新头部,因为整个项目都需要重新编译

正如您可能已经注意到的,我们将objC文件存储在java文件旁边,以使构建规则能够编写,因为xcode不支持在输出文件中使用一些更复杂的宏,但这并不理想,因此我们提出了一个简单的解决方案,以便在需要时更容易地从目录中删除所有objC文件(例如,由于库中的某些更改,我们需要将java文件重新添加到项目中)

解决方案是在清理项目时删除objc文件。您只需要创建一个新的外部构建目标,因为这是清理项目时执行某些操作的唯一方法。在xcode 9中,它被命名为“外部构建系统”

这就是它的样子:

#! /bin/sh

if [ "${ACTION}" == "clean" ]
then
    rm -f "${PROJECT_DIR}/Proj.h"
    find "${PROJECT_DIR}/../src/main" -type f -name '*.h' -delete
    find "${PROJECT_DIR}/../src/main" -type f -name '*.m' -delete
fi

然后,您需要将clean目标添加到j2objc框架的依赖项中,以便它在构建和清理时实际运行。

我们在一个单独的框架中构建j2objc部分,该框架是我们正在使用的java库的一部分。重点是使用不同的方法将java转换为objC,而不是使用build需要注意的是,我们确实需要能够在xcode内部调试java代码,因此j2objc团队建议的外部目标方法对我们不起作用,而构建规则方法确实让我们慢了下来

我们在编译源代码阶段之前添加了构建阶段:

"${PROJECT_DIR}/scripts/j2objc.sh" "${PROJECT_DIR}" "${J2OBJC_HOME}" "${JAVA_SOURCE}";
j2objc.sh:

#!/bin/sh

if [ -z ${1} ]; then
    echo "error: PROJECT_DIR is not set."
    exit 1;
fi;

if [ -z ${2} ]; then
    echo "error: J2OBJC_HOME is not set."
    exit 1;
fi;

if [ -z ${3} ]; then
    echo "error: JAVA_SOURCE is not set."
    exit 1;
fi; 

PROJECT_DIR=$1
J2OBJC_HOME=$2
JAVA_SOURCE=$3


if [ ! -f "${J2OBJC_HOME}/j2objc" ]; then
    echo "J2OBJC_HOME is not correctly defined, currently set to '${J2OBJC_HOME}'";
    exit 1;
fi;

SHOULD_COMPILE=$(find "${JAVA_SOURCE}" -name '*.java' | {
        while read filename; do 
            JAVA_PATH="${filename}";
            JAVA_RELATIVE_PATH=$(sed -e "s|^$JAVA_SOURCE||" <<< "${JAVA_PATH}");
            BASE_RELATIVE_PATH=$(sed -e "s|.java$||" <<< "${JAVA_RELATIVE_PATH}");
            BASE_ABSOLUTE_PATH="${JAVA_SOURCE}/${BASE_RELATIVE_PATH}";
            H_PATH="${BASE_ABSOLUTE_PATH}.h";
            M_PATH="${BASE_ABSOLUTE_PATH}.m";

            if [ ! -f "${H_PATH}" ] || [ ! -f "${M_PATH}" ] || [ "${H_PATH}" -ot "${JAVA_PATH}" ] || [ "${M_PATH}" -ot "${JAVA_PATH}" ]; then
                echo "1";
                break;
            fi;
        done
    }
)

if [ "$SHOULD_COMPILE" = "1" ]; then
    "${J2OBJC_HOME}/j2objc" \
        -d "${JAVA_SOURCE}" \
        -sourcepath "${JAVA_SOURCE}" \
        -classpath "${J2OBJC_HOME}/lib/jsr305-3.0.0.jar" \
        --swift-friendly \
        --strip-reflection \
        --no-segmented-headers \
        -use-arc \
        --static-accessor-methods \
        --nullability \
        --prefixes "${PROJECT_DIR}/prefixes.properties" \
        -g \
        `find "${JAVA_SOURCE}" -name '*.java'`;
fi;
使用这种方法实际实现的是更快的j2objc翻译,因为您不需要为每个要翻译的文件执行单独的j2objc实例,而是告诉一个j2objc实例一次翻译所有java文件,最后,您只需使用构建规则告诉xcode翻译的文件在哪里,这样它们就可以被合并您应该只将java文件添加到项目中,而不是objC文件,它们将根据构建规则进行编译

我们使用脚本自动生成objC文件到框架中的导入,它也在构建阶段定义,并且必须在编译源代码构建阶段之后:

#!/bin/sh

PROJ_HEADER="${PROJECT_DIR}/Proj.h";
PROJ_TMP_HEADER="${PROJECT_DIR}/Proj.h.tmp";
HEADERS_PATH="${PROJECT_DIR}/../src/main/java/";

cat /dev/null > "${PROJ_TMP_HEADER}";
echo "// Generated by Proj external build target

#ifndef proj_h
#define proj_h

#import \"Proj/Bridge.h\"
" >> "${PROJ_TMP_HEADER}";
find "${PROJECT_DIR}/../src/main/java" -type f -name '*.h' | sed -e "s|^$HEADERS_PATH||" | sort | awk '{print "#import \"" $0 "\""}' >> "${PROJ_TMP_HEADER}";
echo "

#endif" >> "${PROJ_TMP_HEADER}";

FILE1=`cat "${PROJ_HEADER}" 2>/dev/null`;
FILE2=`cat "${PROJ_TMP_HEADER}"`;

if [ "$FILE1" = "$FILE2" ]; then
    rm -f "${PROJ_TMP_HEADER}";
else
    mv "${PROJ_TMP_HEADER}" "${PROJ_HEADER}";
fi;
最后的逻辑是这样的,我们不会在每个构建中更新头部,因为整个项目都需要重新编译

正如您可能已经注意到的,我们将objC文件存储在java文件旁边,以使构建规则能够编写,因为xcode不支持在输出文件中使用一些更复杂的宏,但这并不理想,因此我们提出了一个简单的解决方案,以便在需要时更容易地从目录中删除所有objC文件(例如,由于库中的某些更改,我们需要将java文件重新添加到项目中)

解决方案是在清理项目时删除objc文件。您只需要创建一个新的外部构建目标,因为这是清理项目时执行某些操作的唯一方法。在xcode 9中,它被命名为“外部构建系统”

这就是它的样子:

#! /bin/sh

if [ "${ACTION}" == "clean" ]
then
    rm -f "${PROJECT_DIR}/Proj.h"
    find "${PROJECT_DIR}/../src/main" -type f -name '*.h' -delete
    find "${PROJECT_DIR}/../src/main" -type f -name '*.m' -delete
fi

然后,您需要将clean目标添加到j2objc框架的依赖项中,以便它在构建和清理时实际运行。

我们使用了与您类似的方法。但是,我认为目前我们使用的是更好的方法:

  • 我们有单独的框架和Java作为你们。然而,我们创建了一个工具,它可以用Java文件监视文件夹中的变化,并自动将它们转换成*.m。它还可以检查头是否和你们一样改变,而不需要重新编译整个项目
  • 我们使用Cocoapods将生成的文件与iOS应用程序集成。我们同时包含*.m和*.java,以便能够调试java代码。*.java的规则是空的,与您的规则类似
  • 这种在*.java更改时自动生成*.m的方法有一个巨大的优势——代码竞争可以在不需要在xCode中重新编译项目的情况下工作。对于xCode,我们只需要处理另一个.m文件,不需要使用脚本等。根据我们的经验,xCode可以更好地处理这一问题。我们只需要在需要时运行pod安装r我们添加新的*.java文件或进行一些重构