java命令行构建
我们平时编写和编译Java代码都是用ide,或用构建工具,ant或maven等.
但编译代码归根到底是用jdk的原始命令,如javac,java等。工具用多了,基本的处理都不懂了,这在遇到一些新情况或新工具的时候会捉襟见肘。
用java命令行是怎么做到 ant,maven等工具的作用的.
一个简单的javac编译
- 新建两个文件夹,src和 build
src/com/yp/test/HelloWorld.java
build/
├─build
└─src
└─com
└─yp
└─test
HelloWorld.java
- java文件非常简单
package com.yp.test;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("helloWorld");
}
}
- 编译:
javac src/com/yp/test/HelloWorld.java -d build
-d 表示编译到 build文件夹下
- 查看build文件夹
├─build
│ └─com
│ └─yp
│ └─test
│ HelloWorld.class
│
└─src
└─com
└─yp
└─test
HelloWorld.java
- 运行文件
E:\codeplace\n_learn\java\javacmd> java com/yp/test/HelloWorld.class
错误: 找不到或无法加载主类 build.com.yp.test.HelloWorld.class
- 运行时要指定main
E:\codeplace\n_learn\java\javacmd\build> java com.yp.test.HelloWorld
helloWorld
如果多个类来编译,怎样安排路径呢 ?
编译
E:\codeplace\n_learn\java\javacmd>javac src/com/yp/test/HelloWorld.java -sourcepath src -d build -g
-sourcepath 表示 从指定的源文件目录中打到依赖项
运行,注意:运行在build目录下
E:\codeplace\n_learn\java\javacmd\build>java com.yp.test.HelloWorld
怎么打成jar包?
- 生成:
E:\codeplace\n_learn\java\javacmd\build>jar cvf h.jar *
- 运行:
E:\codeplace\n_learn\java\javacmd\build>java h.jar
错误: 找不到或无法加载主类 h.jar
- 这个错误是没有指定main类,所以类似这样来指定:
E:\codeplace\n_learn\java\javacmd\build>java -cp h.jar com.yp.test.HelloWorld
生成可以运行的jar包
需要指定jar包的应用程序入口点,用-e选项:
E:\codeplace\n_learn\java\javacmd\build> jar cvfe h.jar com.yp.test.HelloWorld *
已添加清单
正在添加: com/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: com/yp/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: com/yp/test/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: com/yp/test/entity/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: com/yp/test/entity/Cat.class(输入 = 545) (输出 = 319)(压缩了 41%)
正在添加: com/yp/test/HelloWorld.class(输入 = 844) (输出 = 487)(压缩了 42%)
直接运行
java -jar h.jar
- 额外发现
指定了Main类后,jar包里面的 META-INF/MANIFEST.MF 是这样的, 比原来多了一行Main-Class….
Manifest-Version: 1.0
Created-By: 1.8.0 (Oracle Corporation)
Main-Class: com.yp.test.HelloWorld
如果类里有引用jar包呢?
先下一个jar包 这里直接下 log4j
* main函数改成
import com.yp.test.entity.Cat;
import org.apache.log4j.Logger;
public class HelloWorld {
static Logger log = Logger.getLogger(HelloWorld.class);
public static void main(String[] args) {
Cat c = new Cat("keyboard");
log.info("这是log4j");
System.out.println("hello," + c.getName());
}
}
现的文件是这样的
├─build
├─lib
│ log4j-1.2.17.jar
│
└─src
└─com
└─yp
└─test
│ HelloWorld.java
│
└─entity
Cat.java
- 这个时候 javac命令要接上 -cp ./lib/*.jar
E:\codeplace\n_learn\java\javacmd>javac -encoding "utf8" src/com/yp/test/HelloWorld.java -sourcepath src -d build -g -cp ./lib/*.jar
- 运行,
要加上-cp, -cp 选项貌似会把工作目录给换了, 所以要加上 ;../build
E:\codeplace\n_learn\java\javacmd\build>java -cp ../lib/log4j-1.2.17.jar;../build com.yp.test.HelloWorld
结果:
log4j:WARN No appenders could be found for logger(com.yp.test.HelloWorld).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
hello,keyboard
由于没有 log4j的配置文件,所以提示上面的问题,往 build 里面加上 log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="stdout" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n" />
</layout>
</appender>
<root>
<level value="info" />
<appender-ref ref="stdout" />
</root>
</log4j:configuration>
再运行
E:\codeplace\n_learn\java\javacmd>java -cp lib/log4j-1.2.17.jar;build com.yp.tes t.HelloWorld
15:19:57,359 INFO [HelloWorld] 这是log4j
hello,keyboard
- 说明:
这个log4j配置文件,习惯的做法是放在src目录下, 在编译过程中 copy到build中的,但根据ant的做法,不是用javac的,而是用来处理,我猜测javac是不能copy的,如果想在命令行直接 使用,应该是用cp命令主动去执行 copy操作,
我们要用命令行来实现 源文件-> 可运行jar,
我们可以参考ant的打jar包脚本。
ant是怎怎样构建的?
源文件->字节码->可执行jar
源代码和上一篇是一样的,就多了个build.xml
- 源文件,配置和lib
│ build.xml
│
├─build
├─conf
│ log4j.xml
│
├─lib
│ log4j-1.2.17.jar
│
└─src
└─com
└─yp
└─test
│ HelloWorld.java
│
└─entity
Cat.java
- build.xml内容
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义一个工程,默认任务为jarFile。 -->
<project name="myh" default="jarFile" basedir=".">
<!-- 定义属性,打成jar包的名称。 -->
<property name="jarFileName" value="myh.jar"></property>
<!-- 定义路径,编译java文件时用到的jar包。 -->
<path id="project.lib">
<fileset dir="${basedir}/lib">
<include name="**/*.jar"/>
</fileset>
</path>
<!-- 定义任务,清空任务:清空原有的class文件,创建新的build路径。 -->
<target name="clean">
<delete dir="${basedir}/build" />
<mkdir dir="${basedir}/build" />
</target>
<!-- 定义任务,编译src文件夹中的java文件,编译后的class文件放到创建的文件夹下。 -->
<target name="compile" depends="clean">
<javac encoding="utf-8" srcdir="${basedir}/src" destdir="${basedir}/build" includeantruntime="false">
<classpath refid="project.lib">
</classpath>
</javac>
<copy todir="${basedir}/build">
<fileset dir="${basedir}/conf">
<include name="**/**.*" />
<exclude name="**/*.java"/>
</fileset>
</copy>
</target>
<!-- 定义默认任务,将class文件集合成jar包。 -->
<target name="jarFile" depends="compile">
<!-- 删除原有jar包。 -->
<delete dir="${basedir}/${jarFileName}" />
<!-- 建立新jar包。 -->
<jar destfile="${basedir}/${jarFileName}"
basedir = "${basedir}/build"
includes = "**/**.*"
>
<manifest>
<attribute name="Main-Class" value="com.yp.test.HelloWorld"/>
<attribute name="Class-Path" value="lib/log4j-1.2.17.jar"/>
</manifest>
</jar>
</target>
</project>
- 结果会产生一个myh.jar
- 运行与结果显示正常:
E:\codeplace\n_learn\java\javacmd>java -jar myh.jar
10:43:51,453 INFO [HelloWorld] 这是log4j
hello,keyboard
- 说明
我们查看下jar包里面的MANIFEST.MF:
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.10.0
Created-By: 1.8.0-b132 (Oracle Corporation)
Main-Class: com.yp.test.HelloWorld
Class-Path: lib/log4j-1.2.17.jar
我们打jar的时候,MANIFEST.MF也要搞成这样!!才能查运行
现在我们用命令行构建
我们直接用Python来写这些 各种各样的命令
参考ant,会有,init,clean,compile,jar
废话不多说,直接上代码:
#!/usr/bin/python
#coding:UTF-8
import paramiko,datetime,os,logging
import os,shutil
import sys
import time
# 遍历文件夹
def getFiles(dir, suffix):
res = []
for root, directory, files in os.walk(dir):
for filename in files:
name, suf = os.path.splitext(filename)
if suf == suffix:
res.append(os.path.join(root, filename))
return res
# init
jarFileName = "myh.jar"
basedir = os.getcwd()
mainclass = "com.yp.test.HelloWorld"
# clean
logging.error("当前工作目录:"+os.getcwd()+",清理build")
shutil.rmtree(basedir+"\\"+"build")
os.mkdir(basedir+"\\"+"build")
# compile
logging.error("开始compile")
# javac -encoding "utf8" src/com/yp/test/HelloWorld.java -sourcepath src -d build -g -cp ./lib/*.jar
# os.system("javac -encoding utf8 src/com/yp/test/HelloWorld.java -sourcepath src -d build -g -cp ./lib/*.jar")
# 把文件列表加入到sourcefiles 文件中
file=open('sourcefiles','w')
for tfile in getFiles(basedir+'/src/', '.java'):
file.writelines (tfile+'\n')
file.close()
# 执行编译
os.system("javac -encoding utf8 -sourcepath src @sourcefiles -d build -cp ./lib/*.jar ")
# logging.error("开始copy配置文件")
os.system("cp conf/* build/")
# jar
# 用python生成清单文件
jarfile=open('manifest.mf','w')
jarfile.writelines('Class-Path: ')
for tfile in getFiles(basedir+'/lib/', '.jar'):
tmppath,filename = os.path.split(tfile)
tmpfilepath = 'lib/'+filename
logging.error(tmpfilepath)
jarfile.writelines(tmpfilepath)
jarfile.writelines('\n\n')
jarfile.close()
# 打jar包
os.chdir(basedir+"/build")
os.system("jar cvfem "+basedir+"/"+jarFileName+" "+mainclass+" "+basedir+"/manifest.mf *")
os.chdir(basedir)
# 清理
os.remove(basedir+"/manifest.mf")
os.remove(basedir+"/sourcefiles")
需要的入参
jarFileName = “myh.jar”
mainclass = “com.yp.test.HelloWorld”
使用情况:
一个python build.py命令,一个Java -jar myh.jar
E:\codeplace\n_learn\java\javacmd>python build.py
ERROR:root:褰撳墠宸ヤ綔鐩綍:E:\codeplace\n_learn\j
ERROR:root:寮€濮媍ompile
ERROR:root:lib/log4j-1.2.17.jar
已添加清单
正在添加: com/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: com/yp/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: com/yp/test/(输入 = 0) (输出 = 0)(存储了 0
正在添加: com/yp/test/entity/(输入 = 0) (输出 = 0)(存
正在添加: com/yp/test/entity/Cat.class(输入 = 416) (
正在添加: com/yp/test/HelloWorld.class(输入 = 961) (
正在添加: log4j.xml(输入 = 1220) (输出 = 512)(压缩了
E:\codeplace\n_learn\java\javacmd>java -jar myh.jar
17:26:59,424 INFO [HelloWorld] 这是log4j
hello,keyboard
说明
- javac 不能把源文件目录直接入参
要把 src的java文件全都搞成列表后入参给javac,
这里用python来生成src 目录和子目录下的 java 列表,扔到一个文件sourcefiles
sourcefiles会生成如下,过后会清理
E:\codeplace\n_learn\java\javacmd/src/com\yp\test\HelloWorld.java
E:\codeplace\n_learn\java\javacmd/src/com\yp\test\entity\Cat.java
- 代码:https://github.com/huawumingguo/javacmd/
- jar 添加清单文件 不生效
后来发现,,,是需要有回车(这个非常坑爹,错误也不提示)!!!关于清单文件,参考:http://www.jianshu.com/p/61cfa1347894
这里的思路是先 用python生成清单文件manifest.mf,然后jar命令中加上.
manifest.mf会自动生成lib里面的jar包(最后会清理掉),如下
Class-Path: lib/log4j-1.2.17.jar