date:20170728
使用命令行编译器
Solidity代码库的构建对象之一是solc
,Solidity的命令行编译器。使用solc --help
可以解释所有参数。编译器可以生成不同的输出,范围从简单的二进制和汇编一个抽象语法树(解析树)到预估gas的用量。如果你只想要编译单个文件,你只是想编译单个文件,使用命令solc --bin sourceFile.sol
可以生成二进制。在你发布代码之前,如果想要在编译的时候使用优化器,可以使用命令solc --optimize --bin sourceFile.sol
。如果你想要获取solc
的一些更加高级的输出变量,你可以让它输出所有的信息到一个单独的文件中,使用命令solc -o outputDirectory --bin --ast --asm sourceFile.sol
。
命令行编译器会自动的从文件系统中导入文件,但是可能通过如下的方式,使用prefix=path
来重定向:
solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ =/usr/local/lib/fallback file.sol
这条代码本质上是让编译器在/usr/local/lib/dapp-bin
中查找所有github.com/ethereum/dapp-bin/
开头的文件。如果没有找到文件,会在/usr/lcoal/lib/fallback
目录里查找(空白的前缀总是会匹配)。solc
不会在除了制定路径外的其他路径中查找文件,所以像import "/etc/passwd";
只会在你添加了=/
映射之后才会有效。
如果由于映射,会有多个匹配,那么会选择最多匹配的前缀。
由于安全问题,编译器会限制可访问的路径。源码的路径(和子路径)在命令行中指定,并且重定向定义的路径允许声明导入。但是其他会被拒绝。另外的路径(和子路径)可以通过--allow-paths /sample/path,/another/sample/path
来切换。
如果你的合约使用到了库,你会发现字节码中包含__LibraryName______
形式的字符串。你可以使用solc
作为连接器,会在这些地方插入库的地址。
或者在命令行中添加--libraries "Math:0x12345678901234567890 Heap:0xabcdef0123456"
参数来为每个库提供地址或者把他们保存在一个文件(一个库一行)里,然后运行solc
,使用--libraries fileName
参数。
如果solc
命令中有--link
参数,所有的输入文件都会当做是没有连接的库(十六进制编码),在以上述的__LibraryName____
处连接(如果输入从stdin读取,它会写到stdout中)。这种情况下,所有参数,除了--libraries
都会被忽略(包含-o
)。
如果solc
命令中有--standard-json
,它会在标准输入中接收JSON输入(会在下文叙述),并且在标准输出中返回JSON输出。
编译输入和输出的JSON描述
这些JSON格式会被用作编译器的api,也可以通过solc
使用。这些可能会有更改,有些字段是可选的(已经说过了)。但是只会做一些后向兼容的更改。
编译器API期望JSON格式的输入并输出JSON格式的编译结果。
当然,注释是不允许的,这里只是为了说明情况。
输入描述
{
// 必须字段:源码语言,可以是"Solidity", "serpent", "lll", "assembly", 等等
language: "Solidity",
// 必须字段
sources:
{
// 键是源文件的“全局”名称。
// imports可以使用其他重定向的文件(看下文)
"myFile.sol":
{
// 可选的参数: 源文件的keccak256 hash值
// 如果它被导入,它会被用来验证获得的内容。
"keccak256": "0x123...",
// 必须字段(除非使用了“content”字段,看下文): 源文件的URL(s)
// URL(s) 应该按顺序导入,并且结果要进行keccak256验证。如果hash值不匹配,或者不能正确加载URL(s),就会产生一个错误。
"urls":
[
"bzzr://56ab...",
"ipfs://Qma...",
"file:///tmp/path/to/file.sol"
]
},
"mortal":
{
// 可选字段:源文件的keccak256 hash值
Optional: keccak256 hash of the source file
"keccak256": "0x234...",
// 必须字段(除非“urls”字段被使用): 源文件的字面量信息
"content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }"
}
},
// 可选字段
settings:
{
// 可选字段: 排过序的重定向
remappings: [ ":g/dir" ],
// 可选字段: 优化器设置(enabled默认为false)
optimizer: {
enabled: true,
runs: 500
},
// 元数据设置 (可选的)
metadata: {
// 只能使用字面量内容,并且不能有URLs(默认不支持)。
useLiteralContent: true
},
// 库的地址。如果这里没有给出所有的库,这会导致生成没有连接的对象,这些对象的输出是不同的。
libraries: {
// 最上层的键是所需库的源文件的名称。
// 如果使用了重定向,这些源文件会在映射作用之后匹配全局路径。
// 如果键是空字符串,他会被引用到全局层级。
"myFile.sol": {
"MyLib": "0x123123..."
}
}
// 下面的参数可以用来指定期望的输出。
// 如果没有这个字段,编译器会载入并做类型检测,但是不会生成其他输出。除非出现错误。
// 第一层的键是文件的名称,第二层是合约的名称,如果合约的名称为空,会引用文件的名称。
// 星号代表着所有合约
//
// 有效的输出类型如下所示:
// abi - ABI
// ast - 所有源文件的AST
// legacyAST - 所有源文件的legacy AST
// devdoc - 开发文档 (natspec)
// userdoc - 用户手册 (natspec)
// metadata - 元素据
// ir - 在去语法糖之前的新汇编形式
// evm.assembly - 在去语法糖之后的新汇编形式
// evm.legacyAssembly - JSON格式的旧类型的汇编形式
// evm.bytecode.object - 字节码对象
// evm.bytecode.opcodes - 操作码列表
// evm.bytecode.sourceMap - 源码映射(调试很有用)
// evm.bytecode.linkReferences - 引用连接 (如果是没有连接的对象)
// evm.deployedBytecode* - 发布字节码 (和evm.bytecode有相同的选项)
// evm.methodIdentifiers - 函数列表的hash
// evm.gasEstimates - gas估计函数
// ewasm.wast - eWASM S-表达式 格式 (不支持 atm)
// ewasm.wasm - eWASM 二进制格式 (不支持 atm)
//
// 注意使用 `evm`, `evm.bytecode`, `ewasm`, 等. 会选择输出的每个目标部分
//
outputSelection: {
// 使能每个简单合约的元数据和字节码
"*": {
"*": [ "metadata", "evm.bytecode" ]
},
// 对文件def,使能 MyContract合约的abi和操作码输出。
"def": {
"MyContract": [ "abi", "evm.opcodes" ]
},
// 使能每个合约的源映射
"*": {
"*": [ "evm.sourceMap" ]
},
// 使能每个文件的legacyAST输出
"*": {
"": [ "legacyAST" ]
}
}
}
}
输出描述
{
// 可选字段:如果没有错误或者警告,就不会生成这个字段。
errors: [
{
// 可选字段:源文件的位置
sourceLocation: {
file: "sourceFile.sol",
start: 0,
end: 100
],
// 强制的: 错误类型,如"TypeError", "InternalCompilerError", "Exception", 等
type: "TypeError",
// 强制的: 发生错误的组件,如"general", "ewasm", 等
component: "general",
// 强制的 ("error" 或者 "warning")
severity: "error",
// 强制的
message: "Invalid keyword"
// 可选的: 格式化的信息,说明出错的源码位置。
formattedMessage: "sourceFile.sol:100: Invalid keyword"
}
],
// 这包含文件层级的输出,它通过 outputSelection 设置来限制或者过滤。
sources: {
"sourceFile.sol": {
// 标识符 (用于源码映射)
id: 1,
// AST 对象
ast: {},
// legacy AST 对象
legacyAST: {}
}
},
// 这里包含合约层级的输出。可以通过 outputSelection 设置来限制或者过滤.
contracts: {
"sourceFile.sol": {
// 如果语言没有合约名称,这个字段像是空字段。
"ContractName": {
// 以太坊合约 ABI. 如果为空,会呈现为空数组。
// 详情查看 https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
abi: [],
// 查看输出文档的元数据 (JSON 字符串连载)
metadata: "{...}",
// 用户手册 (natspec)
userdoc: {},
// 开发文档 (natspec)
devdoc: {},
// 中间件(字符串)
ir: "",
// EVM-相关输出
evm: {
// 汇编 (字符串)
assembly: "",
// 旧格式汇编 (对象)
legacyAssembly: {},
// 字节码和相关详情
bytecode: {
// 十六进制字符串的字节码
object: "00fe",
// 操作码列表 (字符串)
opcodes: "",
// 字符串形式的源码映射。查看源码映射定义。
sourceMap: "",
// 如果给定这个字段,这是未连接的对象
linkReferences: {
"libraryFile.sol": {
// 字节码偏移。连接会在指定位置替换20字节
Byte offsets into the bytecode. Linking replaces the 20 bytes located there.
"Library1": [
{ start: 0, length: 20 },
{ start: 200, length: 20 }
]
}
}
},
// 和上面字段结构相同
deployedBytecode: { },
// 函数列表的哈希
methodIdentifiers: {
"delegate(address)": "5c19a95c"
},
// 函数 gas 估计
gasEstimates: {
creation: {
codeDepositCost: "420000",
executionCost: "infinite",
totalCost: "infinite"
},
external: {
"delegate(address)": "25000"
},
internal: {
"heavyLifting()": "infinite"
}
}
},
// eWASM 相关的输出
ewasm: {
// S-表达式 格式
wast: "",
// 二进制格式 (十六进制字符串)
wasm: ""
}
}
}
}
}