热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

FlutteriOSCI脚本制作

我们必须全力以赴,同时又不抱持任何希望。不管做什么事,都要当它是全世界最重要的一件事,但同时又知道这件事根本无关紧要。前言分析了一堆的内

我们必须全力以赴,同时又不抱持任何希望。不管做什么事,都要当它是全世界最重要的一件事,但同时又知道这件事根本无关紧要。



前言

分析了一堆的内容,其实主要就是为了做Flutter的CI的事情。让我们在本文中探讨如何制作Flutter CI for iOS。


踩坑的本机环境


注意对比下我的环境和你的环境是否一样,有些问题在Flutter的新版本中已经被修复了。


➜ app git:(master) ✗ flutter --version
Flutter 1.9.1+hotfix.6 • channel stable • https://github.com/flutter/flutter.git
Framework • revision cc949a8e8b (3 weeks ago) • 2019-09-27 15:04:59 -0700
Engine • revision b863200c37
Tools • Dart 2.5.0

铺垫

本文基于我的前几篇文章的分析所得,如果你不清楚Flutter的编译产物和编译流程,建议阅读我的前几篇文章:


文章说明
Flutter build ios产物分析介绍Flutter的编译产物以及官方接入方案的产物的编译、组织流程。
Flutter xcode_backend分析承接上文,上文中使用了xcode_backend.sh脚本生成编译产物,本文基于上文做深层次的Flutter混合编译分析和介绍。

初始环境

我配置好了一个临时的分析工程(Native接入Flutter的工程目录结构)。

➜ DevelopProjects tree -L 2 temp
temp
├── Android # Android工程
├── flutter_module # Flutter工程
│ ├── README.md
│ ├── build
│ ├── flutterApp.podspec
│ ├── flutter_01.log
│ ├── flutter_module.iml
│ ├── flutter_module_android.iml
│ ├── flutter_podhelper.rb
│ ├── lib
│ ├── pubspec.lock
│ ├── pubspec.yaml
│ └── test
├── iOS # iOS工程
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ ├── iOS
│ ├── iOS.xcodeproj
│ └── iOS.xcworkspace
└── startBuild.sh # iOS产物CI编译脚本10 directories, 11 files

制作过程

先看一眼编译后的东西:

➜ app ✗ tree build -L 3 # 省略了部分内容
build # 编译目录
├── aot
│ ├── App.framework # Flutter业务层代码
│ ├── App.framework.dSYM.noindex
│ ├── app.dill
│ ├── arm64
│ ├── armv7
│ ├── frontend_server.d
│ └── kernel_compile.d
├── dSYMs.noindex
│ └── App.framework.dSYM
├── ios
│ ├── Debug-iphonesimulator # x86_64架构,Debug
│ ├── Release # 合并Debug-iphonesimulator和Release-iphoneos的产物
│ ├── Release
│ │ ├── libFlutterPluginRegistrant.a
│ │ ├── xxx.a
│ │ └── xxx.a
│ └── Release-iphoneos # arm64 armv7,Release
└── libFlutterPlugins.a # 所有的插件及FlutterPluginRegistrant11 directories, 4 files

启动流程


本脚本可以应用于Jenkints,也可以做成其他的自动化的部分。


根据Flutter build ios产物分析、Flutter xcode_backend分析的分析结果,可以简单定下以下的启动流程,分别为如下流程:


  1. 获取输入的变量
  2. 初始化环境变量
  3. 编译App.framework(Flutter业务层代码的framework,包含了静态资源)
  4. 生成dSYM(iOS编译后的符号表,用户还原符号)
  5. Strip dSYM(剥离符号表)
  6. 编译Flutter资源文件
  7. 编译Flutter插件(将Flutter的插件连同注册制都编译为一个framework,对外仅仅提供注册制的header)
  8. unset环境变量

# 主要流程
main() {# Flutter工程目录cd ./flutter_moduleInitVariable # 获取输入的变量InitEnv # 初始化环境变量BuildAppFramework # 编译App.frameworkGeneratedSYM # 生成dSYMStripdSYM # Strip dSYMBuildFlutterAssets # 编译Flutter资源BuildFlutterPlugin # 编译Flutter插件UnsetVariable # unset环境变量# 退出到原来目录cd -
}# 启动脚本
main

根据上述脚本,逐渐填充函数实现即可。


初始化变量


  • 如果使用Jenkints的话,可以将下述参数配置:

# 用于命令行的输出
EchoDone() {echo ""echo " └─$1"echo ""
}# 初始化从外部输入的变量
InitVariable() {echo " ├──Input variable..."export FLT_PROJ_BUILD_MODE='release' # profile/releaseEcho " ├────FLT_PROJ_BUILD_MODE:${FLT_PROJ_BUILD_MODE}"export FLT_ARCH='armv7+arm64'echo " ├────FLT_ARCH:${FLT_ARCH}"
}

##初始化编译环境

InitEnv() {# 设置Flutter Pub地址export PUB_HOSTED_URL='https://pub.flutter-io.cn'echo " ├────PUB_HOSTED_URL=${PUB_HOSTED_URL}"echo ""echo " ├────Flutter version:"echo ""flutter --versionecho ""echo " ├────Clean flutter building artifacts"echo ""flutter cleanEchoDone "Clean flutter building artifacts done"echo " ├────Fetch flutter project dependences"# 更新依赖flutter pub getflutter packages getEchoDone "Fetch flutter project dependences done"# 工程根目录export FLT_PROJ="$(pwd)"# 编译输出目录export FLT_PROJ_BUILD="${FLT_PROJ}/build"# Flutter编译的ios工程或者.ios的地址export FLT_PROJ_iOS="${FLT_PROJ}/ios"# 如果.ios存在则是Module工程if [[ -e "${FLT_PROJ}/.ios" ]]; thenunset FLT_PROJ_iOSexport FLT_PROJ_iOS="${FLT_PROJ}/.ios"fi# 输出目录export FLT_PROJ_iOS_FLT="${FLT_PROJ_iOS}/Flutter"# 输出环境变量,([]:中括号表示可选)echo " ├────./ -> ${FLT_PROJ}"echo " ├────./build -> ${FLT_PROJ_BUILD}"echo " ├────./[.]ios -> ${FLT_PROJ_iOS}"echo " ├────./[.]ios/Flutter -> ${FLT_PROJ_iOS_FLT}"# 需要创建文件夹用于存放生成的一些文件# 不然部分文件因为目录不存在而创建失败mkdir -p "${FLT_PROJ_iOS_FLT}"# 清空缓存rm -rf "${FLT_PROJ_iOS_FLT}/App.framework"
}

编译业务代码

编译Flutter业务层的代码:

BuildAppFramework() {echo " ├──Building App.framework..."echo ""flutter --suppress-analytics build aot --${FLT_PROJ_BUILD_MODE} \--target-platform=ios \--target="lib/main.dart" \--ios-arch="${FLT_ARCH/+/,}" \--output-dir="${FLT_PROJ_BUILD}/aot"export FLT_PROJ_BUILD_AOT_APP="${FLT_PROJ_BUILD}/aot/App.framework"cp -r "${FLT_PROJ_BUILD_AOT_APP}" "${FLT_PROJ_iOS_FLT}"echo " ├────FLT_PROJ_BUILD_AOT_APP:${FLT_PROJ_BUILD_AOT_APP}"EchoDone "Building App.framework done"
}

生成dSYM

GeneratedSYM() {echo " ├─Generating dSYM file..."echo ""mkdir -p -- "${FLT_PROJ_BUILD}/dSYMs.noindex"xcrun dsymutil -o "${FLT_PROJ_BUILD}/dSYMs.noindex/App.framework.dSYM" "${FLT_PROJ_BUILD_AOT_APP}/App"if [[ $? -ne 0 ]]; thenecho "Failed to generate debug symbols (dSYM) file for ${FLT_PROJ_BUILD_AOT_APP}/App."exit -1fiEchoDone "Generating dSYM file done"
}

剥离dSYM并嵌入Info.plist

StripdSYM() {echo " ├─Stripping debug symbols..."echo ""xcrun strip -x -S "${FLT_PROJ_iOS_FLT}/App.framework/App"if [[ $? -ne 0 ]]; thenecho "Failed to strip ${FLT_PROJ_iOS_FLT}/App.framework/App."exit -1ficp "${FLT_PROJ_iOS_FLT}/AppFrameworkInfo.plist" ${FLT_PROJ_iOS_FLT}/App.framework/Info.plistEchoDone "Stripping debug symbols done"
}

编译Flutter assets

BuildFlutterAssets() {echo " ├─Building flutter_assets..."echo " ├─Assembling Flutter resources..."echo ""flutter --suppress-analytics build bundle --${FLT_PROJ_BUILD_MODE} \--target-platform=ios \--target="lib/main.dart" \--depfile="${FLT_PROJ_BUILD}/snapshot_blob.bin.d" \--asset-dir="${FLT_PROJ_iOS_FLT}/App.framework/flutter_assets" \--precompiledEchoDone "Assembling Flutter resources done"
}

  • --precompiled只有Release版本才会有这个参数。

插件静态库的构建

插件静态库的构建需要用到一个flutter pub get;flutter packages get;flutter build ios的一个产物.flutter-plugins。如果对于.flutter-plugins不理解的,可以查看Flutter build ios产物分析的分析。


插件静态库的编译需要编译两份,一份是arm64 armv7(真机),一份是x86_64(模拟器)



  • arm64 armv7:真机Release

/usr/bin/env xcrun xcodebuild BUILD_DIR \-configuration Release ARCHS='arm64 armv7' \-target ${plugin_name} BUILD_DIR=../../build/ios \-sdk iphoneos \-quiet

  • x86_64:用于虚拟机调试用

/usr/bin/env xcrun xcodebuild build \-configuration Debug ARCHS='x86_64' \-target ${plugin_name} BUILD_DIR=../../build/ios \-sdk iphonesimulator -quiet

  • 合并产物

lipo -create "../../build/ios/Debug-iphonesimulator/${plugin_name}/lib${plugin_name}.a" "../../build/ios/Release-iphoneos/${plugin_name}/lib${plugin_name}.a" -o "${plugin_lib_path}"

生成插件库

将源码编译为静态库,输出在./build/ios/Debug-iphonesimulator./build/ios/Release-iphoneos

# 生成静态库
GenerateStaticFramewrok() {local pluginName=$1/usr/bin/env xcrun xcodebuild build \-configuration Release ARCHS='arm64 armv7' \-target "${pluginName}" BUILD_DIR="${FLT_PROJ_BUILD}/ios" \-sdk iphoneos \-quiet >/dev/null/usr/bin/env xcrun xcodebuild build \-configuration Debug ARCHS='x86_64' \-target "${pluginName}" BUILD_DIR="${FLT_PROJ_BUILD}/ios" \-sdk iphonesimulator \-quiet >/dev/null
}

合并插件库

# 合并静态库
MergeStaticFramework() {local pluginName=$1local pluginLibPath=$2local debugFramework="${FLT_PROJ_BUILD}/ios/Debug-iphonesimulator/${pluginName}/lib${pluginName}.a"local releaseFramework="${FLT_PROJ_BUILD}/ios/Release-iphoneos/${pluginName}/lib${pluginName}.a"lipo -create "${debugFramework}" "${releaseFramework}" -o "${pluginLibPath}"
}

编译静态库并合并

# 编译静态库
BuildStaticFramework() {local pluginName=$1local pluginLibPath=$2echo " ├───Generating lib${plugin_name}.a"GenerateStaticFramewrok "${plugin_name}"echo " ├───Merging lib${plugin_name}.a"echo " ├───Plugin Lib Path: ${pluginLibPath}"MergeStaticFramework "${plugin_name}" "${pluginLibPath}"echo " └───Merging lib${plugin_name}.a done"echo ""
}

合并所有插件和注册制

这里将Flutter插件都编译成了静态库,并将所有的静态库合并为一个,方便业务方接入。


  • 注册制指的是:FlutterPluginRegistrant,Flutter用于主动注册插件用的库。

# 编译Flutter的插件和FlutterPluginRegistrant
BuildFlutterPlugin() {echo " ├─Building Flutter Plugin..."echo ""cd ${FLT_PROJ_iOS}# 更新插件依赖,可能会遇到依赖冲突的问题╮(╯▽╰)╭,这个就需要自己解决了pod install --no-repo-updatecd -# 进入Flutter/Pods编译插件cd "${FLT_PROJ_iOS}/Pods"if [ -f "${FLT_PROJ}/.flutter-plugins" ]; then# 声明数组保存Plugin的路径declare -a plugin_lib_path_arr# 获取所有的插件名称plugin_arr="$(cat "${FLT_PROJ}/.flutter-plugins" | awk -F "=" '{print $1}') FlutterPluginRegistrant"echo ""# ./build/ios/Releaselocal pluginOutput="${FLT_PROJ_BUILD}/ios/Release"mkdir -p ${pluginOutput}for plugin_name in ${plugin_arr}; doif [ "${plugin_name}" = "FlutterPluginRegistrant" ]; thenecho " └─Building Flutter Plugin done"echo ""echo " ├─Building FlutterPluginRegistrant..."filocal pluginLibPath="${pluginOutput}/lib${plugin_name}.a"BuildStaticFramework "${plugin_name}" "${pluginLibPath}"plugin_lib_path_arr=("${plugin_lib_path_arr[@]}" "${pluginLibPath}")donefiEchoDone "Building FlutterPluginRegistrant done"echo " ├─Merging Flutter Plugin and FlutterPluginRegistrant..."# 生成在build目录下libtool -static -o "${FLT_PROJ_BUILD}/libFlutterPlugins.a" "${plugin_lib_path_arr[@]}" >/dev/nullEchoDone "Merging Flutter Plugin and FlutterPluginRegistrant done"cd -
}

  • libtool -static -o 合并后的文件路径 aaa.a bbb.a ccc.a...
    • 合并后的文件路径后的所有*.a合并为一个,且链接的类型-static为静态库。
    • 如果不希望不输出has no symbols的警告,可以加上-no_warning_for_no_symbols

完结


  • CI脚本制作完毕!_

完整脚本如下:

EchoDone() {echo ""echo " └─$1"echo ""
}InitVariable() {echo " ├──Input variable..."export FLT_PROJ_BUILD_MODE='release' # profile/releaseEcho " ├────FLT_PROJ_BUILD_MODE:${FLT_PROJ_BUILD_MODE}"export FLT_ARCH='armv7+arm64'echo " ├────FLT_ARCH:${FLT_ARCH}"
}InitEnv() {# 设置Flutter Pub地址export PUB_HOSTED_URL='https://pub.flutter-io.cn'echo " ├────PUB_HOSTED_URL=${PUB_HOSTED_URL}"echo ""echo " ├────Flutter version:"echo ""flutter --versionecho ""echo " ├────Clean flutter building artifacts"echo ""flutter cleanEchoDone "Clean flutter building artifacts done"echo " ├────Fetch flutter project dependences"echo ""# 更新依赖flutter pub getflutter packages getEchoDone "Fetch flutter project dependences done"# 如果.ios存在则是Module工程export FLT_PROJ="$(pwd)"# 编译输出目录export FLT_PROJ_BUILD="${FLT_PROJ}/build"# Flutter编译的ios工程或者.ios的地址export FLT_PROJ_iOS="${FLT_PROJ}/ios"# 如果.ios存在则是Module工程if [[ -e "${FLT_PROJ}/.ios" ]]; thenunset FLT_PROJ_iOSexport FLT_PROJ_iOS="${FLT_PROJ}/.ios"fi# 输出目录export FLT_PROJ_iOS_FLT="${FLT_PROJ_iOS}/Flutter"echo " ├────./ -> ${FLT_PROJ}"echo " ├────./build -> ${FLT_PROJ_BUILD}"echo " ├────./[.]ios -> ${FLT_PROJ_iOS}"echo " ├────./[.]ios/Flutter -> ${FLT_PROJ_iOS_FLT}"# 需要创建文件夹用于存放生成的一些文件# 不然部分文件因为目录不存在而创建失败mkdir -p "${FLT_PROJ_iOS_FLT}"# 清空缓存rm -rf "${FLT_PROJ_iOS_FLT}/App.framework"
}BuildAppFramework() {echo " ├──Building App.framework..."echo ""flutter --suppress-analytics build aot --${FLT_PROJ_BUILD_MODE} \--target-platform=ios \--target="lib/main.dart" \--ios-arch="${FLT_ARCH/+/,}" \--output-dir="${FLT_PROJ_BUILD}/aot"export FLT_PROJ_BUILD_AOT_APP="${FLT_PROJ_BUILD}/aot/App.framework"cp -r "${FLT_PROJ_BUILD_AOT_APP}" "${FLT_PROJ_iOS_FLT}"echo " ├────FLT_PROJ_BUILD_AOT_APP:${FLT_PROJ_BUILD_AOT_APP}"EchoDone "Building App.framework done"
}GeneratedSYM() {echo " ├─Generating dSYM file..."echo ""mkdir -p -- "${FLT_PROJ_BUILD}/dSYMs.noindex"xcrun dsymutil -o "${FLT_PROJ_BUILD}/dSYMs.noindex/App.framework.dSYM" "${FLT_PROJ_BUILD_AOT_APP}/App"if [[ $? -ne 0 ]]; thenecho "Failed to generate debug symbols (dSYM) file for ${FLT_PROJ_BUILD_AOT_APP}/App."exit -1fiEchoDone "Generating dSYM file done"
}StripdSYM() {echo " ├─Stripping debug symbols..."echo ""xcrun strip -x -S "${FLT_PROJ_iOS_FLT}/App.framework/App"if [[ $? -ne 0 ]]; thenecho "Failed to strip ${FLT_PROJ_iOS_FLT}/App.framework/App."exit -1ficp "${FLT_PROJ_iOS_FLT}/AppFrameworkInfo.plist" ${FLT_PROJ_iOS_FLT}/App.framework/Info.plistEchoDone "Stripping debug symbols done"
}BuildFlutterAssets() {echo " ├─Building flutter_assets..."echo " ├─Assembling Flutter resources..."echo ""flutter --suppress-analytics build bundle --${FLT_PROJ_BUILD_MODE} \--target-platform=ios \--target="lib/main.dart" \--depfile="${FLT_PROJ_BUILD}/snapshot_blob.bin.d" \--asset-dir="${FLT_PROJ_iOS_FLT}/App.framework/flutter_assets" \--precompiledEchoDone "Assembling Flutter resources done"
}# 生成静态库
GenerateStaticFramewrok() {local pluginName=$1/usr/bin/env xcrun xcodebuild build \-configuration Release ARCHS='arm64 armv7' \-target "${pluginName}" BUILD_DIR="${FLT_PROJ_BUILD}/ios" \-sdk iphoneos \-quiet >/dev/null/usr/bin/env xcrun xcodebuild build \-configuration Debug ARCHS='x86_64' \-target "${pluginName}" BUILD_DIR="${FLT_PROJ_BUILD}/ios" \-sdk iphonesimulator \-quiet >/dev/null
}# 合并静态库
MergeStaticFramework() {local pluginName=$1local pluginLibPath=$2local debugFramework="${FLT_PROJ_BUILD}/ios/Debug-iphonesimulator/${pluginName}/lib${pluginName}.a"local releaseFramework="${FLT_PROJ_BUILD}/ios/Release-iphoneos/${pluginName}/lib${pluginName}.a"lipo -create "${debugFramework}" "${releaseFramework}" -o "${pluginLibPath}"
}# 编译静态库
BuildStaticFramework() {local pluginName=$1local pluginLibPath=$2echo " ├───Generating lib${plugin_name}.a"GenerateStaticFramewrok "${plugin_name}"echo " ├───Merging lib${plugin_name}.a"echo " ├───Plugin Lib Path: ${pluginLibPath}"MergeStaticFramework "${plugin_name}" "${pluginLibPath}"echo " └───Merging lib${plugin_name}.a done"echo ""
}# 编译Flutter的插件和FlutterPluginRegistrant
BuildFlutterPlugin() {echo " ├─Building Flutter Plugin..."echo ""cd ${FLT_PROJ_iOS}# 更新插件依赖pod installcd -# 进入Flutter/Pods编译插件cd "${FLT_PROJ_iOS}/Pods"if [ -f "${FLT_PROJ}/.flutter-plugins" ]; then# 声明数组保存Plugin的路径declare -a plugin_lib_path_arr# 获取所有的插件名称plugin_arr="$(cat "${FLT_PROJ}/.flutter-plugins" | awk -F "=" '{print $1}') FlutterPluginRegistrant"echo ""# ./build/ios/Releaselocal pluginOutput="${FLT_PROJ_BUILD}/ios/Release"mkdir -p ${pluginOutput}for plugin_name in ${plugin_arr}; doif [ "${plugin_name}" = "FlutterPluginRegistrant" ]; thenecho " └─Building Flutter Plugin done"echo ""echo " ├─Building FlutterPluginRegistrant..."filocal pluginLibPath="${pluginOutput}/lib${plugin_name}.a"BuildStaticFramework "${plugin_name}" "${pluginLibPath}"plugin_lib_path_arr=("${plugin_lib_path_arr[@]}" "${pluginLibPath}")donefiEchoDone "Building FlutterPluginRegistrant done"echo " ├─Merging Flutter Plugin and FlutterPluginRegistrant..."# 生成在build目录下libtool -static -o "${FLT_PROJ_BUILD}/libFlutterPlugins.a" "${plugin_lib_path_arr[@]}" >/dev/nullEchoDone "Merging Flutter Plugin and FlutterPluginRegistrant done"cd -
}# 重置环境变量
UnsetVariable() {echo "Project ${FLT_PROJ} built and packaged successfully."unset FLT_ARCH # 编译的架构unset FLT_PROJ_BUILD_MODE # 编译的模式:release/profileunset PUB_HOSTED_URL # flutter pub域名unset FLT_PROJ # Flutter工程./根目录unset FLT_PROJ_iOS # Flutter工程./iOS目录unset FLT_PROJ_iOS_FLT # Flutter工程./iOS/Flutter目录unset FLT_PROJ_BUILD # Flutter工程./build目录unset FLT_PROJ_BUILD_AOT_APP # Flutter工程./build/aot/App.framework目录
}# 主要流程
main() {# Flutter工程目录cd ./flutter_moduleInitVariable # 获取输入的变量InitEnv # 初始化环境变量BuildAppFramework # 编译App.frameworkGeneratedSYM # 生成dSYMStripdSYM # Strip dSYMBuildFlutterAssets # 编译Flutter资源BuildFlutterPlugin # 编译Flutter插件UnsetVariable # unset环境变量# 退出到原来目录cd -
}# 启动脚本
main

附录


  • 闲鱼flutter混合工程持续集成的最佳实践-源于知乎博客
  • 闲鱼flutter混合工程持续集成最佳实践-源于语雀
  • Flutter iOS 混合工程自动化:介绍了iOS混合工程的方案,方案基本和闲鱼的一致。
  • flutter混合工程持续集成的最佳实践:介绍了Flutter混合工程的方式,和闲鱼的方案基本一致。

iOS


  • iOS 开发中的『库』(一)
    • 如果对于iOS的库不甚了解,可以看看这篇文章了解下。

推荐阅读
  • flutter插件搜索及最新依赖包查找
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了flutter插件搜索及最新依赖包查找相关的知识,希望对你有一定的参考价值。一.flutter中包和插件搜索平台 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • 1,启动MySQL终端输入mysql-uroot-p然后输入密码,启动成功WelcometotheMySQLmonitor.Commandsendwith;or\g.YourMyS ... [详细]
  • kepserver中文手册,kepserver使用教程,kepserver设置
    下面介绍一下KepServer模拟器的使用,以下示例使用服务器随附的Simulator驱动程序来演示创建、配置和运行项目的过程。Simulator驱动程序是基于内存的驱动程序,能为 ... [详细]
  • socket.io是个基于node.js的快平台实时通讯框架。只用不到10行代码,就可以搭建一个简单的多人实时聊天室。先来看看运行后的效果:socket.io多人聊天室只要简单几 ... [详细]
  • docker是跨平台的?_跨平台构建 Docker 镜像新姿势,x86、arm 一把梭
    点击阅读原文可以获得更好的阅读体验。前言在工作和生活中,我们可能经常需要将某个程序跑在不同的CPU架构上,比如让某些不可描述的软件运行在树莓派或嵌入 ... [详细]
  • 先下载linuxhttp:cdimage.ubuntu.comreleases14.04release我下载的是ubuntu-14.04.3-desktop-amd64mac.is ... [详细]
  • [USACO 2006 November Gold] 玉米地Corn Fields
    题目描述  FarmerJohn新买了一块长方形的牧场,这块牧场被划分成M行N列(1<M<12;1<N<12),每一格都是一块正方形的土地。FJ打 ... [详细]
  • 突然觉得服务器ssh密码登录总是浪费一定量的时间,就想试试用sshKey进行登录。生成服务器sshkey和本地sshkey$ssh-keygen在服务器上生成一个authorize ... [详细]
  • 一、域名解析记录说明记录类型A:用来指定域名的IPv4地址(如:8.8.8.8),如果需要将域名指向一个IP ... [详细]
  • git-canal:错误修改
    问题:2016-05-0422:53:48.848[destinationexample,address127.0.0.1:3306,EventParser]ERRO ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 本文介绍了一个React Native新手在尝试将数据发布到服务器时遇到的问题,以及他的React Native代码和服务器端代码。他使用fetch方法将数据发送到服务器,但无法在服务器端读取/获取发布的数据。 ... [详细]
  • 本文介绍了关系型数据库和NoSQL数据库的概念和特点,列举了主流的关系型数据库和NoSQL数据库,同时描述了它们在新闻、电商抢购信息和微博热点信息等场景中的应用。此外,还提供了MySQL配置文件的相关内容。 ... [详细]
author-avatar
昙檀禅潺_162
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有