Centos安装Syncthing同步工具

Syncthing是一个开源的同步工具,支持多版本控制,同时支持Windows、Mac OS X、Linux等客户端,和Resilio有点类似,但是又略有不同,这篇文章介绍一下Centos安装Syncthing工具的方法。

syncthing

一、下载与安装

Syncthing工具配置非常的简单,小z博客以CentOS X64为例,如果您需要其它版本的客户端请访问:syncthing官网下载。言归正传,下面就开始分别执行命令:


### 下载客户端
wget http://soft.hixz.org/linux/syncthing-linux-amd64-v0.14.11.tar.gz
### 解压
tar -zxvf syncthing-linux-amd64-v0.14.11.tar.gz
### 进入目录
cd syncthing-linux-amd64-v0.14.11
### 复制到环境变量
cp syncthing /usr/local/bin/

接着我们需要先运行一次让Syncthing自动生成初始配置文件,上面已经加入环境变量,直接输入syncthing即可运行,会看到下面的运行结果。


[root@xiaoz ~]# syncthing
[monitor] 20:37:05 INFO: Starting syncthing
[start] 20:37:05 INFO: Generating ECDSA key and certificate for syncthing...
[7NYBG] 20:37:05 INFO: syncthing v0.14.11 "Dysprosium Dragonfly" (go1.7.3 linux-amd64) jenkins@build.syncthing.net 2016-11-15 06:23:48 UTC
[7NYBG] 20:37:05 INFO: My ID: 7NYBGD4-AL5FI6M-6P5ULKJ-QSPFASO-T57T4QW-WETWQXT-CAGTJ2I-3PFQGQP
[7NYBG] 20:37:06 INFO: Single thread hash performance is 154 MB/s using minio/sha256-simd (95 MB/s using crypto/sha256).
[7NYBG] 20:37:06 INFO: Default folder created and/or linked to new config
[7NYBG] 20:37:06 INFO: Defaults saved. Edit /root/.config/syncthing/config.xml to taste or use the GUI
[7NYBG] 20:37:06 INFO: Ready to synchronize sxdwy-d7npj (readwrite)
[7NYBG] 20:37:06 INFO: Using discovery server https://discovery-v4-2.syncthing.net/v2/?id=DVU36WY-H3LVZHW-E6LLFRE-YAFN5EL-HILWRYP-OC2M47J-Z4PE62Y-ADIBDQC
[7NYBG] 20:37:06 INFO: Using discovery server https://discovery-v4-3.syncthing.net/v2/?id=VK6HNJ3-VVMM66S-HRVWSCR-IXEHL2H-U4AQ4MW-UCPQBWX-J2L2UBK-NVZRDQZ
[7NYBG] 20:37:06 INFO: Using discovery server https://discovery-v4-4.syncthing.net/v2/?id=LYXKCHX-VI3NYZR-ALCJBHF-WMZYSPK-QG6QJA3-MPFYMSO-U56GTUK-NA2MIAW
[7NYBG] 20:37:06 INFO: Using discovery server https://discovery-v6-2.syncthing.net/v2/?id=DVU36WY-H3LVZHW-E6LLFRE-YAFN5EL-HILWRYP-OC2M47J-Z4PE62Y-ADIBDQC
[7NYBG] 20:37:06 INFO: Using discovery server https://discovery-v6-3.syncthing.net/v2/?id=VK6HNJ3-VVMM66S-HRVWSCR-IXEHL2H-U4AQ4MW-UCPQBWX-J2L2UBK-NVZRDQZ
[7NYBG] 20:37:06 INFO: Using discovery server https://discovery-v6-4.syncthing.net/v2/?id=LYXKCHX-VI3NYZR-ALCJBHF-WMZYSPK-QG6QJA3-MPFYMSO-U56GTUK-NA2MIAW
[7NYBG] 20:37:06 INFO: TCP listener ([::]:22000) starting
[7NYBG] 20:37:06 INFO: Completed initial scan (rw) of folder sxdwy-d7npj
[7NYBG] 20:37:06 INFO: Loading HTTPS certificate: open /root/.config/syncthing/https-cert.pem: no such file or directory
[7NYBG] 20:37:06 INFO: Creating new HTTPS certificate
[7NYBG] 20:37:07 INFO: GUI and API listening on 127.0.0.1:8384
[7NYBG] 20:37:07 INFO: Access the GUI via the following URL: http://127.0.0.1:8384/
[7NYBG] 20:37:07 INFO: Device 7NYBGD4-AL5FI6M-6P5ULKJ-QSPFASO-T57T4QW-WETWQXT-CAGTJ2I-3PFQGQP is "xiaoz" at [dynamic]
[7NYBG] 20:37:11 INFO: Automatic upgrade (current "v0.14.11" < latest "v0.14.12")
[7NYBG] 20:37:17 INFO: Detected 0 NAT devices

二、修改配置文件

上一个步骤输入syncthing已经成功运行,并生成了对应的配置文件,输入Ctrl C退出客户端。我们需要修改下默认的配置文件:vi ~/.config/syncthing/config.xml大概在22行左右的配置,将127.0.0.1修改为0.0.0.0,如下截图。

2016-11-22_204409

三、放行端口

syncthing默认监听8384端口,我们需要在iptables放行这个端口,依次输入下面的命令。


### 放行8384端口
/sbin/iptables -I INPUT -p tcp --dport 8384 -j ACCEPT
/etc/init.d/iptables save
service iptables restart 

四、测试访问

再次输入syncthing命令启动Syncthing客户端,然后在浏览器输入:http://您的服务器IP:8384进行访问。

runsyncthing

Syncthing默认支持中文语言,首次登录会让您设置用户名和密码,到这里基本上就完成了,如何添加其它设备和同步文件夹可以自行研究下。

五、其它说明

如果希望Syncthing在后台运行可以使用nohup命令来实现:nohup syncthing &

六、总结

Syncthing可以在不同设备之间实现同步,前提是已经安装Syncthing客户端,另外还支持历史版本的功能,如果有条件您完整可以利用Syncthing打造自己私有的同步工具。原创文章,转载请注明。

此文参考了:Syncthing: 一个在计算机之间同步文件/文件夹的私密安全同步工具
Syncthing官网:https://syncthing.net/

关于udp的一些问题总结

 

socket 的端口号

客户端的 socket 很少调用 bind() 来指明 socket 的端口号。 相反通常是让操作系统自动分配一个端口号。

TCP客户端 socket 的端口号是在调用了 connect() 之后,系统 会自动分配端口号。

UDP客户端 socket 的端口号是在第一次调用 sendto() 之后, 系统会自动分配端口号。

如果 UDP 的端口是自动分配的话,那么系统不会再改变这个端 口号。如果 UDP 的IP 地址也是自动分配的话,那么每一次调用 sendto() 系统都可能会根据目的 IP 地址而改变源IP地址。

weak end system VS strong end system

网络接口会接收所有和本地 IP 地址一致的数据包,叫做 weak end system .

网络接口只会接收所有和本接口的 IP 地址一致的数据包,叫做 strong end system 。

不同点, 在一个 multihome 的主机有多个网络接口,那么 strong end system 的过滤检查会更强大一些。

UDP 发送数据的地址和接收数据的地址不一致的问题。

UDP 的客户端发送给服务器,如果服务器的用于接收的网络接口 有多个 IP 地址,那么服务器送响应的时候就会自动选择一个 primary IP 地址回送给客户端。primary IP 地址有可能和客户 端发送的 IP 地址不一样。

如果客户端根据接收的响应的 IP 地址来判断是否是服务器发送 的响应,那么就有可能出错。

解决办法,一个是改造客户端,不再用 IP 地址判断是否是一个 服务器发送的数据包,而是根据 DNS 得到的域名来判断。缺点 是系统一定要有域名服务器,而且查询域名会影响效率。 一个办法是服务器不是用 wildcard 来绑定 socket ,而是为每 一个 IP 地址绑定一个 socket 。 缺点是系统如果动态改变了 IP 地址就需要重新起动服务器,而且增加服务器必须 select() 检查所有的 socket 。

UDP ICMP

sendto() 成功返回后,意思是说主机的网络接口有足够的缓冲 队列空间,用以容纳下要发送的数据。

如果接收端回送 ICMP 消息,那么是 socket 不能够知道的,除 非 UDP socket 也调用了 connect() 变成了 connected UDP socket.

linux 的实现中,则会 unconnected socket 也会返回 ICMP 错 误,只要 SO_BSDCOMPAT socket option 没有置位。

ICMP 消息会在下一个 read() 中返回, 错误值是 ECONNREFUSED。

有些 System V 系统有 Bug ,不会为 connected socket 返回 ICMP 错误。

connected UDP socket VS unconnected UDP socket

调用了 connect() 函数的 UDP socket 就从 unconnected UDP socket 变成了 connected UDP socket 。

connect() 并不是真正建立连接,只是把 socket 和一个 peer name 联系在一起。

函数 connect() 函数的操作完全是本地操作, 不涉及网络。

系统会根据 connect() 函数指定的目的地址寻找网络接口,选 择该网络接口的 primary IP 地址作为本地地址。

不同点:

  1. connected socket 调用 send 而不是 sendto 。
  2. connected socket 调用 recv 而不是 recvfrom 。 只有目的地址 和 connect() 函数指定的地址一致才会被接收到。
  • 问题:如果系统也是根据 IP 地址来判断一个连接的话,那么也有可能出现 上面说的发送命令的目的地址和接收响应的源地址是不一样的问题。
  • 答案:只有改造服务器了。
  1. ICMP 错误会收到。
  2. 发送数据的时候,由于不用指定目的 IP 地址,在用户程序 和内核之间传递的数据少,所以效率稍微高一些。

connected UDP socket 是否可以再调用 connect()

和 TCP 不一样,connected socket 可以多次调用 connect()。

connect() 的目的地址中的 address family 如果是 AF_UNSPEC 那么 connected socket 会变成 unconnected socket 。

可能会返回 EAFNOSUPPORT ,但是没有关系。

 

 

 

 

WireShark如何抓取本地localhost的包

今天将自己的电脑既作为客户端又作为服务端进行一个程序的测试,想着用WireShark来抓包分析一下问题,但由于WireShark只能抓取经过电脑网卡的包,由于我是使用localhost或者127.0.0.1进行测试的,流量是不经过电脑网卡的,所以WireShark无法抓包,一番查找之下找到了解决方法。

1 . 以管理员身份打开命令提示符

2 . 输入 route add 本机ip mask 255.255.255.255 网关ip
如果不知道本机ip和网关ip,可以在命令行输入ipconfig查看
例如我的 : route add 192.168.0.106 mask 255.255.255.255 192.168.0.1

这句话的作用是将发给电脑的包转发给路由器,路由器再发给自己的电脑。。避免本地回环

3 . 将我们程序里面的localhost或者127.0.0.1替换成本机ip

4 . 使用WireShark即可抓到本地包

注:在测试完之后,使用route delete 本机ip mask 255.255.255.255 网关ip来删除我们上面的更改,不然我们本机的所有报文都会先经过网卡再回到本机,会比较消耗性能。

 

三、使用 RawCap

需要管理员权限运行 RawCap 。

进入终端(cmd),然后运行:

RawCap.exe 127.0.0.1 dumpfile.pcapRawCap.exe 本地IP dumpfile.pcap

抓好包后,按 Ctrl C,停止抓包。此时会在 RawCap 的同级目录下生成一个dumpfile.pcap文件。用 Wireshark 打开,就可以看到本地环回的数据包了。

四、使用 Npcap

Npcap 是对当前最流行的 WinPcap 工具包进行改进的一个项目。

安装前请先卸载 WinPcap(可以在Wireshark 的Help一栏查看是否在使用 Npcap) 。

安装时要勾选

Use DLT_NULL protocol sa Loopback ...

install npcap in winpcap api-compat mode(选这个,是要兼容 WinPcap)

npcap-0.78-r2

npcap-0.78-r2

安装完成启动 Wireshark, 可以看到在网络接口列表中,多了一项 Npcap Loopback adapter,这个就是来抓本地环回包的网络接口。

wireshark-npcap 捕获界面

wireshark-npcap 捕获界面

不需要钩子,使用 git push 部署网站,

参考资料

  1. Git 2.3 has been released
  2. Git 2.4 — atomic pushes, push to deploy, and more

正文

最近的工作又回到了微信公众号开发(创业团队摸石头,什么都得干。。。),在办公室的电脑上搞了台测试服务器,代码库放在办公室另一台电脑上。方便起见,打算给产品服务器做一个 push-to-deploy 。

所有自己搭建过 git 服务器的人应该都知道,服务器的 git 仓库一般都是 bare 仓库,没有工作目录。而如果不创建成 bare 仓库的话,对当前 branch 的 push 操作都会被拒绝。

以前做 push-to-deploy 的方式是使用 git 的钩子执行脚本,在收到 push 后临时设置仓库的工作目录,检出代码,然后再清除工作目录。虽然也没什么障碍,但总觉得有些不爽就是了。幸运的是,这种额外的配置在 git 2.3 以后就不再是必须的了。

Git 2.3 引入的新特性

Git 2.3 版本以后,如果你向服务器上有工作目录的仓库 push 改动的话,只要服务器的工作目录是干净的(没有未提交的变更),你 push 的改动就会直接体现在服务器的工作目录下。不需要编写钩子脚本,要实现 push-to-deploy 只需要在服务器的仓库改动一个设置就完事了:

$ git config receive.denyCurrentBranch updateInstead

话说这个配置的命令还真是有够直白,好像都没必要特意去记下它了。。。

配置流程

以上,猴子都能懂的 push-to-deploy 攻略总结如下:

  1. 服务器更新 Git 版本到 2.3 以上(建议 2.4 以上,后文解释)
  2. 服务器在选定的网站根目录新建 Git 仓库,不用 bare 。
  3. 服务器在新建的仓库下执行命令:$ git config receive.denyCurrentBranch updateInstead
  4. 客户端 Git 版本随意,在新仓库或原有仓库中新建一个名为 deploy 的 remote repo ,指向服务器仓库地址:git remote add deploy ssh://gaga@foo.bar/path/to/your/document/root
  5. 客户端向 deploy 分支 push 变更,服务器端工作目录随即改变。
  6. 完事

另外一些可能有用的东西

  1. Git 2.4 对这个特性又做了一些补充,加入了一个仅在该特性被触发时会执行的钩子push-to-checkout。对于一些代码更新后需要额外进行一些操作的项目,这是一个十分贴心的小功能。其他更新详见参考资料 2 。
  2. 这种方式会在项目根目录暴露包含项目所有历史的 .git 文件夹,如果项目对此比较敏感的话最好谨慎处理。

windows 安装mysql

出现dll没有

https://www.microsoft.com/zh-CN/download/details.aspx?id=40784

安装mysql

#进入bin目录

cd E:\mysql-5.7.12-winx64\bin   

#直接初始化mysql,生成data文件夹中的文件。

.\mysqld –initialize  自动生成带随机密码的root用户

.\mysqld –initialize-insecure  自动生成无密码的root用户

#指定配置文件和service名称

.\mysqld –install MySQL –defaults-file=D:\Server\mysql\my.ini

 #卸载mysql

.\mysqld –remove

命令:net start mysql          #启动服务器

命令: net stop mysql            #关闭服务器

 

如果发现命令执行后。mysql无法启动。。。注意。。所有的配置都是两个-。。。-   – install

Installing Jenkins OS X Homebrew

Homebrew is the easiest way to install and manage applications on OS X. Let\’s go through how we
install and configure Jenkins.

Installation

Follow the instructions at http://brew.sh/ if you have not had Homebrew installed. Then, let\’s proceed to install Jenkins


$ brew update && brew install jenkins

Jenkins is a Java web application. Download and install the latest Java Runtime Environment manually, or use Homebrew Cask to install it


$ brew cask install java

 

Starting Jenkins

After Jenkins is installed successfully, follow the instructions to start Jenkins on login


$ ln -sfv /usr/local/opt/jenkins/*.plist ~/Library/LaunchAgents

If you want to configure Jenkins to launch on system startup, for all users on OS X, then copy the plist file to the system Launchd location instead


$ sudo cp -fv /usr/local/opt/jenkins/*.plist /Library/LaunchDaemons
$ sudo chown `whoami` /Library/LaunchDaemons/homebrew.mxcl.jenkins.plist

You can always start Jenkins manually with


$ /usr/local/bin/jenkins

or if you have set up your PATH correctly when installing Homebrew, simply


$ jenkins

 

Restarting Jenkins

If you have an older version of Jenkins and you are upgrading it, then you can restart it this way:


$ launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
$ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist

 

Jenkins Options

The default Jenkins startup command in ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist is very simple.


      /usr/bin/java
      -Dmail.smtp.starttls.enable=true
      -jar
      /usr/local/opt/jenkins/libexec/jenkins.war
      --httpListenAddress=127.0.0.1
      --httpPort=8080

 

Launchd starts a java command, listening on only the loopback network interface and using the port 8080 and listen to SMTP connection over TLS.

Network Port

8080 is a common network port for web development. Tomcat defaults to 8080. On my machine, I run Gitbucket on 8080. Let\’s change it to “8181”

--httpPort=8080

 

Java Options

If you have any additional Java runtime options, add them to the configuration file. For example, we want to set:
  • Initial and maximum VM size to 1G
  • Use CMS garbage collector for more responsive system
  • Garbage collect PermGen classes – more details here
  • Limit PermGen size

    -Xms1G
    -Xmx1G

    -XX: UseConcMarkSweepGC
    -XX: CMSClassUnloadingEnabled
    -XX:MaxPermSize=256m

More information for Hotspot Java VM options.

 Jenkins Plugins

A fresh Jenkins installation is ready to be used, especially if you only need to manage Java projects. There are many Jenkins plugins that can make Jenkins much more powerful. To access Plugin Manager, go to JenkinsDashboardPlugin Manager or the url http://jenkins:8181/pluginManager/available.

The easiest way to install a plugin is to view the list of all published plugins in the Plugin Manager, click and Install and restart Jenkins. To install a plugin manually, copy the plugin-name.hpi file to Jenkin\’s directory. In the Homebrew installation, that will be $HOME/.jenkins/plugins. Restart Jenkins and the plugin will be installed.

These are the plugins I have installed:

Jenkins configuration

Besides the launchd configuration file explained above, all other Jenkins configuration is stored in $HOME/.jenkins. The main configuration file is config.xml. You really should not edit the configuration files by hand. Use the Jenkins web interface to make changes http://jenkins:8181/manage

Upgrading Jenkins

As a continuous integration server, Jenkins itself is frequently updated. You can check if a new version of Jenkins is available

$ brew update && brew outdated jenkins

Upgrading jenkins is as easy as

 


$ brew upgrade jenkins

Homebrew\’s default behaviour is to keep older versions. If you don\’t need the older versions of Jenkins anymore, you can remove them

$ brew cleanup jenkins

Preserve Configuration And Plugins

You don\’t want to set up Jenkins all over again on every upgrade. The good news is, Jenkins server files and your configuration are in separate locations. Your new Jenkins installation will automatically pick up your existing configurations and plugins at “$HOME/.jenkins”

启动执行脚本目录所在:

安装包所在:

服务项所在:

可以通过命令开始停止:

// 方法一:
sudo launchctl load /Library/LaunchDaemons/org.jenkins-ci.plist 启动  
   
sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist 停止

如果要修改端口,比如7070,可在第8步重启jenkins前执行以下命令修改端口参数

defaults write /Library/Preferences/org.jenkins-ci httpPort 7070

Jenkins默认安装目录:

/users/share/  

或者更改目录:

cd 到 /Library/LaunchDaemons 编辑 org.jenkins-ci.plist  更改jenkinshome和username
重启Jenkins即可 

然后链接 launchd 配置文件

// 方法二(1): 
$ln -sfv /usr/local/opt/jenkins/*.plist ~/Library/LaunchAgents

可以更改此 plist 来进行一些自定义的配置,详细列表可以参考https://wiki.jenkins-ci.org/display/JENKINS/Starting%20and%20Accessing%20Jenkins

如果要其他机器也可以访问,把 plist 里的--httpListenAddress=127.0.0.1删掉即可

修改完后,在终端执行

// 方法二(2)
$launchctl load ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist

即可启动 Jenkins

接着用浏览器访问localhost:8080(默认配置),就可以看到 Jenkins 的 web 界面了

# 这种方式安装的Jenkins默认目录是/usr/local/Cellar/jenkins/1.651/libexec/....
#所以想让其他局域网用户访问则需要修改/etc/apache2/httpd.conf的ServerRoot 路径  
改为/usr/local/Cellar/ 即可 

进入 系统管理-启用安全-访问控制-Jenkins专有用户数据库-安全矩阵 添加一个用户:

保存之后会在Jenkins安装目录下生成config.xml文件.

<useSecurity>trueuseSecurity>  这个节点表示使用安全管理,也就是需要用户登录才能操作  

用刚才添加的用户进行注册,不使用密码登录可以

<useSecurity>falseuseSecurity>   

即可.


Jenkins修改workspace和build目录

Globally Changing the workspace location for all Jobs

In order of changing the workspace for a single job, there is another option in the Jenkins system settings that allows for changing the workspace for every job.
Navigate to Jenkins->Manage Jenkins->Configure System and click on the AdvancedButton on the right hand side.


Voilà! This opens up the following options section
Now you can change your workspace and build directory to any other location on your machine. Jenkins provides 3 predefined variables that can be used to specify the new location:

  • ${JENKINS_HOME} — Jenkins home directory
  • ${ITEM_ROOTDIR} — Root directory of a job for which the workspace is allocated
  • ${ITEM_FULLNAME} — ‘/’-separated job name, like “foo/bar”

The default value for the “Workspace Root Directory” is ${ITEM_ROOTDIR}/workspace and for the “Build Record Root Directory” it’s ${ITEM_ROOTDIR}/builds.

环境变量配置:

展示二维码图片

二维码图片的URL链接有了,那要怎样才能将二维码图片展示在Jenkins项目的历史构建列表中呢?

这里需要用到另外一个插件,description setter plugin。安装该插件后,在【Post-build Actions】栏目中会多出description setter功能,可以实现构建完成后设置当次build的描述信息。这个描述信息不仅会显示在build页面中,同时也会显示在历史构建列表中。

有了这个前提,要将二维码图片展示在历史构建列表中貌似就可以实现了,能直观想到的方式就是采用HTMLimg标签,将<img src='qr_code_url'>写入到build描述信息中。

这个方法的思路是正确的,不过这么做以后并不会实现我们预期的效果。

这是因为Jenkins出于安全的考虑,所有描述信息的Markup Formatter默认都是采用Plain text模式,在这种模式下是不会对build描述信息中的HTML编码进行解析的。

要改变也很容易,Manage Jenkins -> Configure Global Security,将Markup Formatter的设置更改为Safe HTML即可。

更改配置后,我们就可以在build描述信息中采用HTMLimg标签插入图片了。

另外还需要补充一个点。如果是使用蒲公英(pyger)平台,会发现每次上传安装包后返回的二维码图片是一个短链接,神奇的是这个短连接居然是固定的(对同一个账号而言)。这个短连接总是指向最近生成的二维码图片,但是对于二维码图片的唯一URL地址,平台并没有在响应中进行返回。在这种情况下,我们每次构建完成后保存二维码图片的URL链接就没有意义了。

应对的做法是,每次上传完安装包后,通过返回的二维码图片短链接将二维码图片下载并保存到本地,然后在build描述信息中引用该图片在Jenkins中的地址即可。

收集编译成果物(Artifacts)

每次完成构建后,编译生成的文件较多,但是并不是所有的文件都是我们需要的。

通常情况下,我们可能只需要其中的部分文件,例如.ipa/.app/.plist/.apk等,这时我们可以将这部分文件单独收集起来,并在构建页面中展示出来,以便在需要时进行下载。

要实现这样一个功能,需要在【Post-build Actions】栏目中新增Archive the artifacts,然后在Files to archive中通过正则表达式指定成果物文件的路径。

设置完毕后,每次构建完成后,Jenkins会在Console Output中采用设定的正则表达式进行搜索匹配,如果能成功匹配到文件,则会将文件收集起来。

gradle 执行另一个项目的命令

$gradle build 打全渠道即所有flavor;且含所有buildTypes
$gradle assemble[flavor][buildType]
如:gradle assembleFlavor1Release 如:gradle assembleFlavor2Debug
gradle本身支持命令缩写, 如:gradle assFlavor1R

 

https://github.com/openbakery/gradle-xcodePlugin

1.Adding dependencies to a task

There are several ways you can define the dependencies of a task. In Section 16.5, “Task dependencies”you were introduced to defining dependencies using task names. Task names can refer to tasks in the same project as the task, or to tasks in other projects. To refer to a task in another project, you prefix the name of the task with the path of the project it belongs to. The following is an example which adds a dependency from projectA:taskX to projectB:taskY:

Example 19.11. Adding dependency on task from another project

build.gradle

project('projectA') {
    task taskX(dependsOn: ':projectB:taskY') {
        doLast {
            println 'taskX'
        }
    }
}

project('projectB') {
    task taskY {
        doLast {
            println 'taskY'
        }
    }
}

Output of gradle -q taskX

> gradle -q taskX
taskY
taskX

使用project()和task.execute()

task test2() {
    doLast{
        println "aaaa";
    }
}

task test1() {
    doLast{
        test2.execute()
        project("app").test3.execute();
        project("app").tasks["test3"].execute();
        project("app").tasks.test3.execute();
    }
}

//最后执行的某个任务..一般是清理项目

build.finalizedBy('buildsomethingElse')

2.Running another Gradle build from a build

You can use the GradleBuild task. You can use either of the dir or buildFile properties to specify which build to execute, and the tasks property to specify which tasks to execute.

https://docs.gradle.org/current/userguide/organizing_build_logic.html#sec:external_build

Example 43.6. Running another build from a build

build.gradle

task build(type: GradleBuild) {
    buildFile = 'other.gradle'
    tasks = ['hello']
}

other.gradle

task hello {
    doLast {
        println "hello from the other build."
    }
}

Output of gradle -q build

> gradle -q build
hello from the other build.

3.使用命令行选择build.gradle

https://docs.gradle.org/current/userguide/tutorial_gradle_command_line.html#sec:selecting_build

When you run the gradle command, it looks for a build file in the current directory. You can use the -b option to select another build file. If you use -b option then settings.gradle file is not used. Example:

Example 4.5. Selecting the project using a build file

subdir/myproject.gradle

task hello {
    doLast {
        println "using build file '$buildFile.name' in '$buildFile.parentFile.name'."
    }
}

Output of gradle -q -b subdir/myproject.gradle hello

> gradle -q -b subdir/myproject.gradle hello
using build file 'myproject.gradle' in 'subdir'.

Alternatively, you can use the -p option to specify the project directory to use. For multi-project builds you should use -p option instead of -b option.

Example 4.6. Selecting the project using project directory

Output of gradle -q -p subdir hello

> gradle -q -p subdir hello
using build file 'build.gradle' in 'subdir'.

Android常用的Gradle配置和加速编译

Why Gradle

Gradle makes the impossible possible, the possible easy and the easy elegant.

在Android开发中经常会用Gradle来构建项目,Gradle能很方便的项目的版本集成和打包,虽Gradle官方已经给出很详细的文档了,但还是有必要抽离出一些常用的配置。

整个Android项目的编译依赖于Gradle的编译,虽然前几天发布了Android 2.0 stable,拥有了Instant Run强大的功能,但是有“改一行布局代码Run一次”习惯的同学有必要知道如何加速Gradle的编译。

主要介绍一下:

  • 如何配置Gradle
  • 如何加速Gradle的编译
  • 一些常用的项目构建知识

How Gradle

gradle.properties文件适合配置IDE的属性,当然也适合配置你在项目的关键/敏感参数,因为它将运行在Incubating parallel mode(

孵化并行模式,应该解释为运行时候嵌入在项目当中),也是属于默认的gitignore,这样你的敏感信息(key账号密码,appkey等)就不会被push到git了,你需要注意的是 属性中有中文的话,记得转成unicode来显示,不然可能引发一些莫名的错误

类似于这样,你可以把你的签名keystore的信息,服务端的endpoint,第三方服务的appkey,ide的配置信息放在这。

  KEY_ALIAS=yat3s
  KEYSTORE_PASSWORD=123456
  KEY_PASSWORD=123456
  umeng_appkey_product=adcdefghijk
  umeng_appkey_dev=adcdefghijk
  deepshare_appid=adcdefghijk
  bugly_appid_product=adcdefghijk
  bugly_appid_dev=adcdefghijk
  rong_appkey=adcdefghijk
  endpoint_product=http://api.yat3s.com
  endpoint_dev=http://api.yat3s.com:9000
  app_name=\u006f\u0070\u0065\u006e\u5f00\u8154`

那么配置好这些信息如何在Gradle和Java中使用呢?

其实Groovy语言和Java很接近也很好用,往下看↓

buildTypes

  buildTypes {
   release {
       minifyEnabled true
       shrinkResources true
       signingConfig signingConfigs.release
       proguardFiles getDefaultProguardFile(\'proguard-android.txt\'), \'proguard-rules.pro\'
       buildConfigField "String", "ENDPOINT", "\"${endpoint_product}\""
       resValue "string", "umeng_appkey", "${umeng_appkey_product}"
       resValue "string", "deepshare_appid", "${deepshare_appid}"
       resValue "string", "bugly_appid", "${bugly_appid_product}"
       resValue "string", "rong_appkey", "${rong_appkey}"
       resValue "string", "channel", "product"
       resValue "string", "op_app_name", "${app_name}"
   }
   debug {
       signingConfig signingConfigs.release
       proguardFiles getDefaultProguardFile(\'proguard-android.txt\'), \'proguard-rules.pro\'
       buildConfigField "String", "ENDPOINT", "\"${endpoint_product}\""
       resValue "string", "umeng_appkey", "${umeng_appkey_dev}"
       resValue "string", "deepshare_appid", "${deepshare_appid}"
       resValue "string", "bugly_appid", "${bugly_appid_dev}"
       resValue "string", "rong_appkey", "${rong_appkey}"
       resValue "string", "channel", "dev"
       resValue "string", "op_app_name", "dev_${app_name}"
     }
   }
  • 引用gradle.properties 只需要加 ${key}
  • 定义在 buildConfigField “String”, “ENDPOINT”,”\”${endpoint_product}\””,在Java享用时候只需要读取BuildConfig.ENDPOINT即可
  • 定义在 resValue “string”, “umeng_appkey”, “${umeng_appkey_product}” , 在xml中享用只需要R.string.umeng_appkey即可

记住几个点:

  • 在你确保你minifyEnabled(混淆)没问题的时候debug模式尽量别开minifyEnabled和shrinkResources,这样会大大降低编译速度
  • 如果真要开混淆,把生成mapping关掉
  • 尽量把一些第三方key分成staging和product,这样容错性高,避免你推送给你的用户一个”Test”
  • 定义buildConfigField时候注意双引号的问题 “\”${endpoint_product}\””
  • 如果不方便管理签名可debug和release同一个签名。 如果需要同时安装debug和release包只需要修改applicationId即可

defaultConfig

这里尽量新建一个Conifg.gradle文件来统一管理这些基本配置(在project下右键new file即可) ,

Config.gradle文件如下

ext {
android = [compileSdkVersion: 23,
           buildToolsVersion: "23.0.1",
           applicationId    : "com.yat3s.d3v",
           minSdkVersion    : 15,
           targetSdkVersion : 23,
           versionCode      : 36,
           versionName      : "1.3.1"]
}
dependencies = [
               design              : "com.android.support:design:23.3.0",
               nineoldandroids     : "com.nineoldandroids:library:2.4.0",
               retrofit            : "com.squareup.retrofit:retrofit:2.0.0",
               rxandroid           : "io.reactivex:rxandroid:1.0.0",
               okhttp-urlconnection: "com.squareup.okhttp:okhttp-urlconnection:2.0.0",
               okhttp              : "com.squareup.okhttp:okhttp:2.0.0",
               butterknife         : "com.jakewharton:butterknife:7.0.1"]

然后在项目的build.gradle文件下添加 apply from: “config.gradle”

  apply from: "config.gradle"
  buildscript {
      repositories {
        jcenter()
      }
      dependencies {
        classpath \'com.android.tools.build:gradle:2.0.0\'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
      }
    }       
    allprojects {
      repositories {
        jcenter()
      }
    }       ``

然后你使用起来就简单多了

def config = rootProject.ext.android // 配置
def dep = rootProject.ext.dependencies // 依赖
useLibrary \'org.apache.http.legacy\' // 如果你是api23的话,在用到apache的http库时候,记得加上这个,不然混淆时候因为urlConnection的升级导致异常
compileSdkVersion config.compileSdkVersion
buildToolsVersion config.buildToolsVersion
defaultConfig {
  applicationId config.applicationId
  minSdkVersion config.minSdkVersion
  targetSdkVersion config.targetSdkVersion
  versionCode config.versionCode
  versionName config.versionName
  manifestPlaceholders = [UMENG_CHANNEL_VALUE: "open"]
}

dependencies {
  compile fileTree(dir: \'libs\', include: [\'*.jar\'])
  compile dep.design
  compile dep.retrofit
  compile dep.okhttp
}

那么这样用的好处是什么呢?

  • 如果你有多个Module都需要依赖design包的话,design包一升级导致了多个module需要改版本,你就可能同时引入了多个依赖导致apk增大
  • 统一管理versionCode和versionName
  • 看起来是不是更简洁

signingConfigs

def keystore = file(\'keystore/yat3s.jks\')
signingConfigs {
    release {
        keyAlias KEY_ALIAS
        keyPassword KEY_PASSWORD
        storePassword KEYSTORE_PASSWORD
        storeFile keystore
    }
}
  • 在非资源定义(下面有提到的resValue为资源定义,其他的地方则为非资源定义)下引用gradle.properties可直接输入properties的key
  • 如果你要公开你的源码记得把你的keystore ignore掉,私有库无视该项

packagingOptions

packagingOptions {
  exclude \'META-INF/LICENSE\'
  exclude(\'META-INF/LICENSE.txt\')
  exclude(\'META-INF/NOTICE.txt\')
  exclude \'META-INF/NOTICE\'
  exclude \'META-INF/DEPENDENCIES\'
  // umeng推送的jar包含有的okio库跟okhttp的okio库冲突
  exclude \'META-INF/maven/com.squareup.okio/okio/pom.xml\'
  exclude \'META-INF/maven/com.squareup.okio/okio/pom.properties\'
}
  • 主要处理第三方库在导入时候的一些声明文件冲突,Option this

compileOptions {

compileOptions {
  sourceCompatibility JavaVersion.VERSION_1_7
  targetCompatibility JavaVersion.VERSION_1_7
}
  • 个人觉得如果你不用lambda表达式的话,可以用java1.7,因为1.8还不是完美的支持Android

lintOptions

 lintOptions {
    disable "InvalidPackage"
    disable "MissingTranslation" // 禁用中英文string.xml的强制lint
    lintConfig file("lint.xml")
}

打包管理

def getDate() {
    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC")) // 注意时区
}     

// 获取当前git的Revision
def getRevision() {
    return ext.hash = \'git rev-parse --short HEAD\'.execute().text.trim()
}
productFlavors {
   rc_open {}
   rc_360 {}
   rc_yingyongbao {}
   rc_baidu {}
   rc_91 {}
   rc_wandoujia {}
   rc_anzhuo {}
   rc_xiaomi {}
   rc_meizu {}
   rc_oppo {}
   rc_huawei {}
   rc_weibo {}
   rc_dev {}
   }
productFlavors.all { flavor ->
    // 这里只是方便友盟统计每个渠道的数据
    flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
 }

  // 修改打包后APK的文件名
  applicationVariants.all { variant ->
   variant.outputs.each { output ->
       def oldFile = output.outputFile
       if (variant.buildType.name.equals(\'release\')) {
           // 输出apk名称为yat3s_v1.0_2016-04-12_yingyongbao_a23f2e1.apk
           def releaseApkName = \'yat3s-v\'   defaultConfig.versionName   \'_\'   getDate()   \'_\'   variant.productFlavors[0].name   "_"   getRevision()   \'.apk\'
           output.outputFile = new File(oldFile.parent, releaseApkName)
       }
       if (variant.buildType.name.equals(\'debug\')) {
          // Do nothing
       }
   }
 }
  • 多渠道打包用的是Gradle的productFlavors
  • git的Revision 方便你管理你的release和tag
  • 打包的时候可以用Jenkins来自动Build你的包

Speed Gradle

https://developer.android.com/studio/build/optimize-your-build.html

我们都知道编译项目时候是依赖gradle的,gradle的构建速度决定了你的工作效率,上面零散的提到几点,下面总结一下:

  • 第一个大招 升级Android studio 2.0 并且使用最新版的gradle 2.0 使用Instant Run
  • 在你确保你的混淆没问题时候在debug模式下关闭所有混淆minifyEnabled false, shrinkResources false
  • 尽可能的让你的所有module的依赖库版本一致。
  • 在允许的情况下,在android studio的配置中,开启offline模式,在构建项目时候在命令后面加上–daemon –parallel –offline即可
  • 在添加依赖的时候尽量明确版本号,省去gradle查找最新版的时间,不要compile \’com.facebook.fresco:fresco:latest\’ 或compile \’com.facebook.fresco:fresco:1. \’,
  • 开启守护线程并行编译,在gradle.properties中添加
    org.gradle.parallel=true
    org.gradle.daemon=true
    org.gradle.jvmargs=-Xms256m -Xmx1024m
  • 在gradle中def方法的时候尽量在debug情况下减少耗时操作或者不操作,比如:
    def getRevision() {
      if (!System.getenv(\'CI_BUILD\')) {
        return 0
      }
      return ext.hash = \'git rev-parse --short HEAD\'.execute().text.trim()
    }

GRADLE自定义你的BUILDCONFIG

在Android开发中,我们使用android.util.Log来打印日志,方便我们的开发调试。但是这些代码不想在发布后执行,我们并不想在软件发布后调试日志被其他开发者看到,现在我的方法是设置一个全局变量,标记软件为Debug模式还是Release模式。来看下代码:

public class Log {
    private static final boolean DEBUG = true;

    public static void i(String tag, String msg) {
        if (DEBUG)
            android.util.Log.i(tag, msg);
    }

    public static void e(String tag, String msg) {
        if (DEBUG)
            android.util.Log.e(tag, msg);
    }

    public static void d(String tag, String msg) {
        if (DEBUG)
            android.util.Log.d(tag, msg);
    }

    public static void v(String tag, String msg) {
        if (DEBUG)
            android.util.Log.v(tag, msg);
    }

    public static void w(String tag, String msg) {
        if (DEBUG)
            android.util.Log.w(tag, msg);
    }
}

这样打包发布之前只要改下DEBUG=false就行了,但是每次在发布之前都要手动去改这个变量,不是很方便,而且不排除开发者忘记改的情况。那么有没有更好更方便的做法呢?

首先在Gradle脚本中默认的debug和release两种模式BuildCondig.DEBUG字段分别为true和false,而且不可更改。该字段编译后自动生成,在Studio中生成的目录在 app/build/source/BuildConfig/Build Varients/package name/BuildConfig 文件下。我们以9GAG为例来看下release模式下该文件的内容:

public final class BuildConfig {
  public static final boolean DEBUG = false;
  public static final String APPLICATION_ID = "com.storm.9gag";
  public static final String BUILD_TYPE = "release";
  public static final String FLAVOR = "wandoujia";
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "1.0";
  // Fields from build type: release
  public static final boolean LOG_DEBUG = false;
}

自定义BuildConfig字段

大家看到上述内容的时候发现莫名的有个LOG_DEBUG字段,这个完全是我自定义的一个字段,我来用它控制Log的输出,而没有选择用默认的DEBUG字段。举例一个场景,我们在App开发用到的api环境假设可能会有测试、正式环境,我们不可能所有的控制都通过DEBUG字段来控制,而且有时候环境复杂可能还会有两个以上的环境,这个时候就用到了Gradle提供了自定义BuildConfig字段,我们在程序中通过这个字段就可以配置我们不同的开发环境。

语法很简单:

buildConfigField "boolean", "API_ENV", "true"

上述语法就定义了一个boolean类型的API_ENV字段,值为true,之后我们就可以在程序中使用BuildConfig.API_ENV字段来判断我们所处的api环境。例如:

public class BooheeClient {
    public static final boolean DEBUG = BuildConfig.API_ENV;

    public static String getHost {
        if (DEBUG) {
            return "your qa host";
        }
        return "your production host";
    }
}

不仅如此,如果遇到复杂的环境,你也可能自定义一个String类型的字段,这种方式免去了发布之前手动更改环境的麻烦,减少出错的可能性,只需要在Gradle配置好debug、release等模式下的环境就好了,打包的之后毫无顾虑。

使用方法很简单,大家如果有问题或者疑问可以直接博客留言。