summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2019-10-12 23:06:48 +0800
committerruki <[email protected]>2019-10-12 23:06:48 +0800
commit938be7f39197d2f202fe3342e82d9d31c3264b53 (patch)
tree088215082dc0a89902ba7ff7c97eecaf4f945278
parent3dcbba4360d7a7ab7082117711566a5bfc6857dc (diff)
seperate io submodules
-rw-r--r--xmake/core/base/io.lua682
-rw-r--r--xmake/core/base/io/file.lua525
-rw-r--r--xmake/core/base/io/filelock.lua149
-rw-r--r--xmake/core/base/io/socket.lua77
-rw-r--r--xmake/core/sandbox/modules/io.lua3
5 files changed, 756 insertions, 680 deletions
diff --git a/xmake/core/base/io.lua b/xmake/core/base/io.lua
index 5aaebc1af..4a515c202 100644
--- a/xmake/core/base/io.lua
+++ b/xmake/core/base/io.lua
@@ -19,684 +19,12 @@
--
-- define module
-local io = io or {}
-local _file = _file or {}
-local _filelock = _filelock or {}
-local _socket = _socket or {}
+local io = io or {}
--- load modules
-local path = require("base/path")
-local table = require("base/table")
-local string = require("base/string")
-
--- save metatable and builtin functions
-io._file = _file
-io._filelock = _filelock
-io._socket = _socket
-io._stdfile = io._stdfile or io.stdfile
-
--- new an file
-function _file.new(filepath, fileref)
- local file = table.inherit(_file)
- file._NAME = path.filename(filepath)
- file._PATH = path.absolute(filepath)
- file._FILE = fileref
- setmetatable(file, _file)
- return file
-end
-
--- get the file name
-function _file:name()
- return self._NAME
-end
-
--- get the file path
-function _file:path()
- return self._PATH
-end
-
--- close file
-function _file:close()
- if not self._FILE then
- return false, string.format("file(%s) has been closed!", self:name())
- end
- local ok, errors = io.file_close(self._FILE)
- if ok then
- self._FILE = nil
- end
- return ok, errors
-end
-
--- tostring(file)
-function _file:__tostring()
- return "file: " .. self:name()
-end
-
--- gc(file)
-function _file:__gc()
- if self._FILE and io.file_close(self._FILE) then
- self._FILE = nil
- end
-end
-
--- get file length
-function _file:__len()
- return self:size()
-end
-
--- get file rawfd
-function _file:rawfd()
- if not self._FILE then
- return false, string.format("file(%s) has been closed!", self:name())
- end
- local result, errors = io.file_rawfd(self._FILE)
- if not result and errors then
- errors = string.format("file(%s): %s", self:name(), errors)
- end
- return result, errors
-end
-
--- get file size
-function _file:size()
- if not self._FILE then
- return false, string.format("file(%s) has been closed!", self:name())
- end
- local result, errors = io.file_size(self._FILE)
- if not result and errors then
- errors = string.format("file(%s): %s", self:name(), errors)
- end
- return result, errors
-end
-
--- read data from file
-function _file:read(fmt, opt)
- if not self._FILE then
- return false, string.format("file(%s) has been closed!", self:name())
- end
- opt = opt or {}
- local result, errors = io.file_read(self._FILE, fmt, opt.continuation)
- if errors then
- errors = string.format("file(%s): %s", self:name(), errors)
- end
- return result, errors
-end
-
--- write data to file
-function _file:write(...)
- if not self._FILE then
- return false, string.format("file(%s) has been closed!", self:name())
- end
- local ok, errors = io.file_write(self._FILE, ...)
- if not ok and errors then
- errors = string.format("file(%s): %s", self:name(), errors)
- end
- return ok, errors
-end
-
--- seek offset at file
-function _file:seek(whence, offset)
- if not self._FILE then
- return false, string.format("file(%s) has been closed!", self:name())
- end
- local result, errors = io.file_seek(self._FILE, whence, offset)
- if not result and errors then
- errors = string.format("file(%s): %s", self:name(), errors)
- end
- return result, errors
-end
-
--- flush data to file
-function _file:flush()
- if not self._FILE then
- return false, string.format("file(%s) has been closed!", self:name())
- end
- local ok, errors = io.file_flush(self._FILE)
- if not ok and errors then
- errors = string.format("file(%s): %s", self:name(), errors)
- end
- return ok, errors
-end
-
--- this file is a tty?
-function _file:isatty()
- if not self._FILE then
- return false, string.format("file(%s) has been closed!", self:name())
- end
- local ok, errors = io.file_isatty(self._FILE)
- if ok == nil and errors then
- errors = string.format("file(%s): %s", self:name(), errors)
- end
- return ok, errors
-end
-
--- iterator of lines
-function _file._lines_iter(data)
- local l = data.file:read("l", data.opt)
- if not l and data.opt.close_on_finished then
- data.file:close()
- end
- return l
-end
-
--- read all lines from a file
-function _file:lines(opt)
- return _file._lines_iter, { file = assert(self), opt = opt or {} }
-end
-
--- print file
-function _file:print(...)
- return self:write(string.format(...), "\n")
-end
-
--- printf file
-function _file:printf(...)
- return self:write(string.format(...))
-end
-
--- save object
-function _file:save(object, opt)
- local str, errors = string.serialize(object, opt)
- if errors then
- return false, errors
- else
- return self:write(str)
- end
-end
-
--- load object
-function _file:load()
- local data, err = self:read("*all")
- if err then
- return nil, err
- end
- if data and type(data) == "string" then
- return data:deserialize()
- end
-end
-
--- new a filelock
-function _filelock.new(lockpath, lock)
- local filelock = table.inherit(_filelock)
- filelock._NAME = path.filename(lockpath)
- filelock._PATH = path.absolute(lockpath)
- filelock._LOCK = lock
- filelock._LOCKED_NUM = 0
- setmetatable(filelock, _filelock)
- return filelock
-end
-
--- get the filelock name
-function _filelock:name()
- return self._NAME
-end
-
--- get the filelock path
-function _filelock:path()
- return self._PATH
-end
-
--- is locked?
-function _filelock:islocked()
- return self._LOCKED_NUM > 0
-end
-
--- lock file
---
--- @param opt the argument option, {shared = true}
---
--- @return ok, errors
---
-function _filelock:lock(opt)
- if not self._LOCK then
- return false, string.format("filelock(%s) has been closed!", self:name())
- end
- if self._LOCKED_NUM > 0 or io.filelock_lock(self._LOCK, opt) then
- self._LOCKED_NUM = self._LOCKED_NUM + 1
- return true
- else
- return false, string.format("filelock(%s): lock %s failed!", self:name(), self:path())
- end
-end
-
--- try to lock file
---
--- @param opt the argument option, {shared = true}
---
--- @return ok, errors
---
-function _filelock:trylock(opt)
- if not self._LOCK then
- return false, string.format("filelock(%s) has been closed!", self:name())
- end
- if self._LOCKED_NUM > 0 or io.filelock_trylock(self._LOCK, opt) then
- self._LOCKED_NUM = self._LOCKED_NUM + 1
- return true
- else
- return false, string.format("filelock(%s): trylock %s failed!", self:name(), self:path())
- end
-end
-
--- unlock file
-function _filelock:unlock(opt)
- if not self._LOCK then
- return false, string.format("filelock(%s) has been closed!", self:name())
- end
- if self._LOCKED_NUM > 1 or (self._LOCKED_NUM > 0 and io.filelock_unlock(self._LOCK)) then
- if self._LOCKED_NUM > 0 then
- self._LOCKED_NUM = self._LOCKED_NUM - 1
- else
- self._LOCKED_NUM = 0
- end
- return true
- else
- return false, string.format("filelock(%s): unlock %s failed!", self:name(), self:path())
- end
-end
-
--- close filelock
-function _filelock:close()
- if not self._LOCK then
- return false, string.format("filelock(%s) has been closed!", self:name())
- end
- local ok = io.filelock_close(self._LOCK)
- if ok then
- self._LOCK = nil
- self._LOCKED_NUM = 0
- end
- return ok
-end
-
--- tostring(filelock)
-function _filelock:__tostring()
- return "filelock: " .. self:name()
-end
-
--- gc(filelock)
-function _filelock:__gc()
- if self._LOCK and io.filelock_close(self._LOCK) then
- self._LOCK = nil
- self._LOCKED_NUM = 0
- end
-end
-
--- new a socket
-function _socket.new(sock)
- local socket = table.inherit(_socket)
- socket._SOCK = sock
- socket._NAME = ""
- setmetatable(socket, _socket)
- return socket
-end
-
--- get the socket name
-function _socket:name()
- return self._NAME
-end
-
--- close socket
-function _socket:close()
- if not self._SOCK then
- return false, string.format("socket(%s) has been closed!", self:name())
- end
- local ok = io.socket_close(self._SOCK)
- if ok then
- self._SOCK = nil
- end
- return ok
-end
-
--- tostring(socket)
-function _socket:__tostring()
- return "socket: " .. self:name()
-end
-
--- gc(socket)
-function _socket:__gc()
- if self._SOCK and io.socket_close(self._SOCK) then
- self._SOCK = nil
- end
-end
-
--- read all lines from file
-function io.lines(filepath, opt)
-
- -- close on finished
- opt = opt or {}
- if opt.close_on_finished == nil then
- opt.close_on_finished = true
- end
-
- -- open file
- local file = io.open(filepath, "r", opt)
- if not file then
- return function() return nil end
- end
-
- return file:lines(opt)
-end
-
--- read all data from file
-function io.readfile(filepath, opt)
-
- opt = opt or {}
-
- -- open file
- local file = io.open(filepath, "r", opt)
- if not file then
- -- error
- return nil, string.format("open %s failed!", filepath)
- end
-
- -- read all
- local data, err = file:read("*all", opt)
-
- -- exit file
- file:close()
-
- -- ok?
- return data, err
-end
-
-function io.read(fmt, opt)
- return io.stdin:read(fmt, opt)
-end
-
-function io.write(...)
- return io.stdout:write(...)
-end
-
-function io.print(...)
- return io.stdout:print(...)
-end
-
-function io.printf(...)
- return io.stdout:printf(...)
-end
-
-function io.flush()
- return io.stdout:flush()
-end
-
--- write data to file
-function io.writefile(filepath, data, opt)
-
- -- init option
- opt = opt or {}
-
- -- open file
- local file = io.open(filepath, "w", opt)
- if not file then
- return false, string.format("open %s failed!", filepath)
- end
-
- -- write all
- file:write(data)
-
- -- exit file
- file:close()
-
- -- ok?
- return true
-end
-
--- isatty
-function io.isatty(file)
- file = file or io.stdout
- return file:isatty()
-end
-
--- get std file, /dev/stdin, /dev/stdout, /dev/stderr
-function io.stdfile(filepath)
- local file = nil
- if filepath == "/dev/stdin" then
- file = io._stdfile(1)
- elseif filepath == "/dev/stdout" then
- file = io._stdfile(2)
- elseif filepath == "/dev/stderr" then
- file = io._stdfile(3)
- end
- if file then
- return _file.new(filepath, file)
- else
- return nil, string.format("failed to get std file: %s", filepath)
- end
-end
-
--- open file
-function io.open(filepath, mode, opt)
-
- -- check
- assert(filepath)
-
- -- init option and mode
- opt = opt or {}
- mode = mode or "r"
-
- -- open it
- local file = io.file_open(filepath, mode .. (opt.encoding or ""))
- if file then
- return _file.new(filepath, file)
- else
- return nil, string.format("failed to open file: %s", filepath)
- end
-end
-
--- open a filelock
-function io.openlock(filepath)
-
- -- check
- assert(filepath)
-
- -- open it
- local lock = io.filelock_open(filepath)
- if lock then
- return _filelock.new(filepath, lock)
- else
- return nil, string.format("failed to open lock: %s", filepath)
- end
-end
-
--- open a socket
-function io.opensock()
-
- -- open it
- local sock = io.socket_open()
- if sock then
- return _socket.new(filepath, sock)
- else
- return nil, string.format("failed to open socket!")
- end
-end
-
--- close file
-function io.close(file)
- return (file or io.stdout):close()
-end
-
--- save object the the given filepath
-function io.save(filepath, object, opt)
-
- -- check
- assert(filepath and object)
-
- -- init option
- opt = opt or {}
-
- -- open the file
- local file, err = io.open(filepath, "wb", opt)
- if err then
- -- error
- return false, err
- end
-
- -- save object to file
- local ok, errors = file:save(object, opt)
- -- close file
- file:close()
- if not ok then
- -- error
- return false, string.format("save %s failed, %s!", filepath, errors)
- end
-
- -- ok
- return true
-end
-
--- load object from the given file
-function io.load(filepath, opt)
-
- -- check
- assert(filepath)
-
- -- init option
- opt = opt or {}
-
- -- open the file
- local file, err = io.open(filepath, "rb", opt)
- if err then
- -- error
- return nil, err
- end
-
- -- load object
- local result, errors = file:load()
-
- -- close file
- file:close()
-
- -- ok?
- return result, errors
-end
-
--- gsub the given file and return replaced data
-function io.gsub(filepath, pattern, replace, opt)
-
- -- init option
- opt = opt or {}
-
- -- read all data from file
- local data, errors = io.readfile(filepath, opt)
- if not data then return nil, 0, errors end
-
- -- replace it
- local count = 0
- if type(data) == "string" then
- data, count = data:gsub(pattern, replace)
- else
- return nil, 0, string.format("data is not string!")
- end
-
- -- replace ok?
- if count ~= 0 then
- -- write all data to file
- local ok, errors = io.writefile(filepath, data, opt)
- if not ok then return nil, 0, errors end
- end
-
- -- ok
- return data, count
-end
-
--- cat the given file
-function io.cat(filepath, linecount, opt)
-
- -- init option
- opt = opt or {}
-
- -- open file
- local file = io.open(filepath, "r", opt)
- if file then
-
- -- show file
- local count = 1
- for line in file:lines(opt) do
-
- -- show line
- io.print(line)
-
- -- end?
- if linecount and count >= linecount then
- break
- end
-
- -- update the line count
- count = count + 1
- end
-
- -- exit file
- file:close()
- end
-end
-
--- tail the given file
-function io.tail(filepath, linecount, opt)
-
- -- init option
- opt = opt or {}
-
- -- all?
- if linecount < 0 then
- return io.cat(filepath, opt)
- end
-
- -- open file
- local file = io.open(filepath, "r", opt)
- if file then
-
- -- read lines
- local lines = {}
- for line in file:lines(opt) do
- table.insert(lines, line)
- end
-
- -- tail lines
- local tails = {}
- if #lines ~= 0 then
- local count = 1
- for index = #lines, 1, -1 do
-
- -- show line
- table.insert(tails, lines[index])
-
- -- end?
- if linecount and count >= linecount then
- break
- end
-
- -- update the line count
- count = count + 1
- end
- end
-
- -- show tails
- if #tails ~= 0 then
- for index = #tails, 1, -1 do
-
- -- show tail
- io.print(tails[index])
-
- end
- end
-
- -- exit file
- file:close()
- end
-end
-
--- lazy loading stdfile
-io.stdin = nil
-io.stdout = nil
-io.stderr = nil
-setmetatable(io, { __index = function (tbl, key)
- local val = rawget(tbl, key)
- if val == nil and (key == "stdin" or key == "stdout" or key == "stderr") then
- val = io.stdfile("/dev/" .. key)
- if val ~= nil then
- rawset(tbl, key, val)
- end
- end
- return val
- end})
+-- load io submodules
+require("base/io/file")
+require("base/io/socket")
+require("base/io/filelock")
-- return module
return io
diff --git a/xmake/core/base/io/file.lua b/xmake/core/base/io/file.lua
new file mode 100644
index 000000000..9e37654d7
--- /dev/null
+++ b/xmake/core/base/io/file.lua
@@ -0,0 +1,525 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015 - 2019, TBOOX Open Source Group.
+--
+-- @author ruki
+-- @file file.lua
+--
+
+-- define module
+local io = io or {}
+local _file = _file or {}
+
+-- load modules
+local path = require("base/path")
+local table = require("base/table")
+local string = require("base/string")
+
+-- save the builtin functions
+io._stdfile = io._stdfile or io.stdfile
+
+-- new an file
+function _file.new(filepath, fileref)
+ local file = table.inherit(_file)
+ file._NAME = path.filename(filepath)
+ file._PATH = path.absolute(filepath)
+ file._FILE = fileref
+ setmetatable(file, _file)
+ return file
+end
+
+-- get the file name
+function _file:name()
+ return self._NAME
+end
+
+-- get the file path
+function _file:path()
+ return self._PATH
+end
+
+-- close file
+function _file:close()
+ if not self._FILE then
+ return false, string.format("file(%s) has been closed!", self:name())
+ end
+ local ok, errors = io.file_close(self._FILE)
+ if ok then
+ self._FILE = nil
+ end
+ return ok, errors
+end
+
+-- tostring(file)
+function _file:__tostring()
+ return "file: " .. self:name()
+end
+
+-- gc(file)
+function _file:__gc()
+ if self._FILE and io.file_close(self._FILE) then
+ self._FILE = nil
+ end
+end
+
+-- get file length
+function _file:__len()
+ return self:size()
+end
+
+-- get file rawfd
+function _file:rawfd()
+ if not self._FILE then
+ return false, string.format("file(%s) has been closed!", self:name())
+ end
+ local result, errors = io.file_rawfd(self._FILE)
+ if not result and errors then
+ errors = string.format("file(%s): %s", self:name(), errors)
+ end
+ return result, errors
+end
+
+-- get file size
+function _file:size()
+ if not self._FILE then
+ return false, string.format("file(%s) has been closed!", self:name())
+ end
+ local result, errors = io.file_size(self._FILE)
+ if not result and errors then
+ errors = string.format("file(%s): %s", self:name(), errors)
+ end
+ return result, errors
+end
+
+-- read data from file
+function _file:read(fmt, opt)
+ if not self._FILE then
+ return false, string.format("file(%s) has been closed!", self:name())
+ end
+ opt = opt or {}
+ local result, errors = io.file_read(self._FILE, fmt, opt.continuation)
+ if errors then
+ errors = string.format("file(%s): %s", self:name(), errors)
+ end
+ return result, errors
+end
+
+-- write data to file
+function _file:write(...)
+ if not self._FILE then
+ return false, string.format("file(%s) has been closed!", self:name())
+ end
+ local ok, errors = io.file_write(self._FILE, ...)
+ if not ok and errors then
+ errors = string.format("file(%s): %s", self:name(), errors)
+ end
+ return ok, errors
+end
+
+-- seek offset at file
+function _file:seek(whence, offset)
+ if not self._FILE then
+ return false, string.format("file(%s) has been closed!", self:name())
+ end
+ local result, errors = io.file_seek(self._FILE, whence, offset)
+ if not result and errors then
+ errors = string.format("file(%s): %s", self:name(), errors)
+ end
+ return result, errors
+end
+
+-- flush data to file
+function _file:flush()
+ if not self._FILE then
+ return false, string.format("file(%s) has been closed!", self:name())
+ end
+ local ok, errors = io.file_flush(self._FILE)
+ if not ok and errors then
+ errors = string.format("file(%s): %s", self:name(), errors)
+ end
+ return ok, errors
+end
+
+-- this file is a tty?
+function _file:isatty()
+ if not self._FILE then
+ return false, string.format("file(%s) has been closed!", self:name())
+ end
+ local ok, errors = io.file_isatty(self._FILE)
+ if ok == nil and errors then
+ errors = string.format("file(%s): %s", self:name(), errors)
+ end
+ return ok, errors
+end
+
+-- iterator of lines
+function _file._lines_iter(data)
+ local l = data.file:read("l", data.opt)
+ if not l and data.opt.close_on_finished then
+ data.file:close()
+ end
+ return l
+end
+
+-- read all lines from a file
+function _file:lines(opt)
+ return _file._lines_iter, { file = assert(self), opt = opt or {} }
+end
+
+-- print file
+function _file:print(...)
+ return self:write(string.format(...), "\n")
+end
+
+-- printf file
+function _file:printf(...)
+ return self:write(string.format(...))
+end
+
+-- save object
+function _file:save(object, opt)
+ local str, errors = string.serialize(object, opt)
+ if errors then
+ return false, errors
+ else
+ return self:write(str)
+ end
+end
+
+-- load object
+function _file:load()
+ local data, err = self:read("*all")
+ if err then
+ return nil, err
+ end
+ if data and type(data) == "string" then
+ return data:deserialize()
+ end
+end
+
+-- read all lines from file
+function io.lines(filepath, opt)
+
+ -- close on finished
+ opt = opt or {}
+ if opt.close_on_finished == nil then
+ opt.close_on_finished = true
+ end
+
+ -- open file
+ local file = io.open(filepath, "r", opt)
+ if not file then
+ return function() return nil end
+ end
+
+ return file:lines(opt)
+end
+
+-- read all data from file
+function io.readfile(filepath, opt)
+
+ opt = opt or {}
+
+ -- open file
+ local file = io.open(filepath, "r", opt)
+ if not file then
+ -- error
+ return nil, string.format("open %s failed!", filepath)
+ end
+
+ -- read all
+ local data, err = file:read("*all", opt)
+
+ -- exit file
+ file:close()
+
+ -- ok?
+ return data, err
+end
+
+function io.read(fmt, opt)
+ return io.stdin:read(fmt, opt)
+end
+
+function io.write(...)
+ return io.stdout:write(...)
+end
+
+function io.print(...)
+ return io.stdout:print(...)
+end
+
+function io.printf(...)
+ return io.stdout:printf(...)
+end
+
+function io.flush()
+ return io.stdout:flush()
+end
+
+-- write data to file
+function io.writefile(filepath, data, opt)
+
+ -- init option
+ opt = opt or {}
+
+ -- open file
+ local file = io.open(filepath, "w", opt)
+ if not file then
+ return false, string.format("open %s failed!", filepath)
+ end
+
+ -- write all
+ file:write(data)
+
+ -- exit file
+ file:close()
+
+ -- ok?
+ return true
+end
+
+-- isatty
+function io.isatty(file)
+ file = file or io.stdout
+ return file:isatty()
+end
+
+-- get std file, /dev/stdin, /dev/stdout, /dev/stderr
+function io.stdfile(filepath)
+ local file = nil
+ if filepath == "/dev/stdin" then
+ file = io._stdfile(1)
+ elseif filepath == "/dev/stdout" then
+ file = io._stdfile(2)
+ elseif filepath == "/dev/stderr" then
+ file = io._stdfile(3)
+ end
+ if file then
+ return _file.new(filepath, file)
+ else
+ return nil, string.format("failed to get std file: %s", filepath)
+ end
+end
+
+-- open file
+function io.open(filepath, mode, opt)
+
+ -- check
+ assert(filepath)
+
+ -- init option and mode
+ opt = opt or {}
+ mode = mode or "r"
+
+ -- open it
+ local file = io.file_open(filepath, mode .. (opt.encoding or ""))
+ if file then
+ return _file.new(filepath, file)
+ else
+ return nil, string.format("failed to open file: %s", filepath)
+ end
+end
+
+-- close file
+function io.close(file)
+ return (file or io.stdout):close()
+end
+
+-- save object the the given filepath
+function io.save(filepath, object, opt)
+
+ -- check
+ assert(filepath and object)
+
+ -- init option
+ opt = opt or {}
+
+ -- open the file
+ local file, err = io.open(filepath, "wb", opt)
+ if err then
+ -- error
+ return false, err
+ end
+
+ -- save object to file
+ local ok, errors = file:save(object, opt)
+ -- close file
+ file:close()
+ if not ok then
+ -- error
+ return false, string.format("save %s failed, %s!", filepath, errors)
+ end
+
+ -- ok
+ return true
+end
+
+-- load object from the given file
+function io.load(filepath, opt)
+
+ -- check
+ assert(filepath)
+
+ -- init option
+ opt = opt or {}
+
+ -- open the file
+ local file, err = io.open(filepath, "rb", opt)
+ if err then
+ -- error
+ return nil, err
+ end
+
+ -- load object
+ local result, errors = file:load()
+
+ -- close file
+ file:close()
+
+ -- ok?
+ return result, errors
+end
+
+-- gsub the given file and return replaced data
+function io.gsub(filepath, pattern, replace, opt)
+
+ -- init option
+ opt = opt or {}
+
+ -- read all data from file
+ local data, errors = io.readfile(filepath, opt)
+ if not data then return nil, 0, errors end
+
+ -- replace it
+ local count = 0
+ if type(data) == "string" then
+ data, count = data:gsub(pattern, replace)
+ else
+ return nil, 0, string.format("data is not string!")
+ end
+
+ -- replace ok?
+ if count ~= 0 then
+ -- write all data to file
+ local ok, errors = io.writefile(filepath, data, opt)
+ if not ok then return nil, 0, errors end
+ end
+
+ -- ok
+ return data, count
+end
+
+-- cat the given file
+function io.cat(filepath, linecount, opt)
+
+ -- init option
+ opt = opt or {}
+
+ -- open file
+ local file = io.open(filepath, "r", opt)
+ if file then
+
+ -- show file
+ local count = 1
+ for line in file:lines(opt) do
+
+ -- show line
+ io.print(line)
+
+ -- end?
+ if linecount and count >= linecount then
+ break
+ end
+
+ -- update the line count
+ count = count + 1
+ end
+
+ -- exit file
+ file:close()
+ end
+end
+
+-- tail the given file
+function io.tail(filepath, linecount, opt)
+
+ -- init option
+ opt = opt or {}
+
+ -- all?
+ if linecount < 0 then
+ return io.cat(filepath, opt)
+ end
+
+ -- open file
+ local file = io.open(filepath, "r", opt)
+ if file then
+
+ -- read lines
+ local lines = {}
+ for line in file:lines(opt) do
+ table.insert(lines, line)
+ end
+
+ -- tail lines
+ local tails = {}
+ if #lines ~= 0 then
+ local count = 1
+ for index = #lines, 1, -1 do
+
+ -- show line
+ table.insert(tails, lines[index])
+
+ -- end?
+ if linecount and count >= linecount then
+ break
+ end
+
+ -- update the line count
+ count = count + 1
+ end
+ end
+
+ -- show tails
+ if #tails ~= 0 then
+ for index = #tails, 1, -1 do
+
+ -- show tail
+ io.print(tails[index])
+
+ end
+ end
+
+ -- exit file
+ file:close()
+ end
+end
+
+-- lazy loading stdfile
+io.stdin = nil
+io.stdout = nil
+io.stderr = nil
+setmetatable(io, { __index = function (tbl, key)
+ local val = rawget(tbl, key)
+ if val == nil and (key == "stdin" or key == "stdout" or key == "stderr") then
+ val = io.stdfile("/dev/" .. key)
+ if val ~= nil then
+ rawset(tbl, key, val)
+ end
+ end
+ return val
+ end})
+
diff --git a/xmake/core/base/io/filelock.lua b/xmake/core/base/io/filelock.lua
new file mode 100644
index 000000000..8efbfd554
--- /dev/null
+++ b/xmake/core/base/io/filelock.lua
@@ -0,0 +1,149 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015 - 2019, TBOOX Open Source Group.
+--
+-- @author ruki
+-- @file filelock.lua
+--
+
+-- define module
+local io = io or {}
+local _filelock = _filelock or {}
+
+-- load modules
+local path = require("base/path")
+local table = require("base/table")
+local string = require("base/string")
+
+-- new a filelock
+function _filelock.new(lockpath, lock)
+ local filelock = table.inherit(_filelock)
+ filelock._NAME = path.filename(lockpath)
+ filelock._PATH = path.absolute(lockpath)
+ filelock._LOCK = lock
+ filelock._LOCKED_NUM = 0
+ setmetatable(filelock, _filelock)
+ return filelock
+end
+
+-- get the filelock name
+function _filelock:name()
+ return self._NAME
+end
+
+-- get the filelock path
+function _filelock:path()
+ return self._PATH
+end
+
+-- is locked?
+function _filelock:islocked()
+ return self._LOCKED_NUM > 0
+end
+
+-- lock file
+--
+-- @param opt the argument option, {shared = true}
+--
+-- @return ok, errors
+--
+function _filelock:lock(opt)
+ if not self._LOCK then
+ return false, string.format("filelock(%s) has been closed!", self:name())
+ end
+ if self._LOCKED_NUM > 0 or io.filelock_lock(self._LOCK, opt) then
+ self._LOCKED_NUM = self._LOCKED_NUM + 1
+ return true
+ else
+ return false, string.format("filelock(%s): lock %s failed!", self:name(), self:path())
+ end
+end
+
+-- try to lock file
+--
+-- @param opt the argument option, {shared = true}
+--
+-- @return ok, errors
+--
+function _filelock:trylock(opt)
+ if not self._LOCK then
+ return false, string.format("filelock(%s) has been closed!", self:name())
+ end
+ if self._LOCKED_NUM > 0 or io.filelock_trylock(self._LOCK, opt) then
+ self._LOCKED_NUM = self._LOCKED_NUM + 1
+ return true
+ else
+ return false, string.format("filelock(%s): trylock %s failed!", self:name(), self:path())
+ end
+end
+
+-- unlock file
+function _filelock:unlock(opt)
+ if not self._LOCK then
+ return false, string.format("filelock(%s) has been closed!", self:name())
+ end
+ if self._LOCKED_NUM > 1 or (self._LOCKED_NUM > 0 and io.filelock_unlock(self._LOCK)) then
+ if self._LOCKED_NUM > 0 then
+ self._LOCKED_NUM = self._LOCKED_NUM - 1
+ else
+ self._LOCKED_NUM = 0
+ end
+ return true
+ else
+ return false, string.format("filelock(%s): unlock %s failed!", self:name(), self:path())
+ end
+end
+
+-- close filelock
+function _filelock:close()
+ if not self._LOCK then
+ return false, string.format("filelock(%s) has been closed!", self:name())
+ end
+ local ok = io.filelock_close(self._LOCK)
+ if ok then
+ self._LOCK = nil
+ self._LOCKED_NUM = 0
+ end
+ return ok
+end
+
+-- tostring(filelock)
+function _filelock:__tostring()
+ return "filelock: " .. self:name()
+end
+
+-- gc(filelock)
+function _filelock:__gc()
+ if self._LOCK and io.filelock_close(self._LOCK) then
+ self._LOCK = nil
+ self._LOCKED_NUM = 0
+ end
+end
+
+-- open a filelock
+function io.openlock(filepath)
+
+ -- check
+ assert(filepath)
+
+ -- open it
+ local lock = io.filelock_open(filepath)
+ if lock then
+ return _filelock.new(filepath, lock)
+ else
+ return nil, string.format("failed to open lock: %s", filepath)
+ end
+end
+
diff --git a/xmake/core/base/io/socket.lua b/xmake/core/base/io/socket.lua
new file mode 100644
index 000000000..cee11fc51
--- /dev/null
+++ b/xmake/core/base/io/socket.lua
@@ -0,0 +1,77 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015 - 2019, TBOOX Open Source Group.
+--
+-- @author ruki
+-- @file socket.lua
+--
+
+-- define module
+local io = io or {}
+local _socket = _socket or {}
+
+-- load modules
+local table = require("base/table")
+local string = require("base/string")
+
+-- new a socket
+function _socket.new(sock)
+ local socket = table.inherit(_socket)
+ socket._SOCK = sock
+ socket._NAME = ""
+ setmetatable(socket, _socket)
+ return socket
+end
+
+-- get the socket name
+function _socket:name()
+ return self._NAME
+end
+
+-- close socket
+function _socket:close()
+ if not self._SOCK then
+ return false, string.format("socket(%s) has been closed!", self:name())
+ end
+ local ok = io.socket_close(self._SOCK)
+ if ok then
+ self._SOCK = nil
+ end
+ return ok
+end
+
+-- tostring(socket)
+function _socket:__tostring()
+ return "socket: " .. self:name()
+end
+
+-- gc(socket)
+function _socket:__gc()
+ if self._SOCK and io.socket_close(self._SOCK) then
+ self._SOCK = nil
+ end
+end
+
+-- open a socket
+function io.opensock()
+
+ -- open it
+ local sock = io.socket_open()
+ if sock then
+ return _socket.new(filepath, sock)
+ else
+ return nil, string.format("failed to open socket!")
+ end
+end
diff --git a/xmake/core/sandbox/modules/io.lua b/xmake/core/sandbox/modules/io.lua
index ab2fc71f1..d6603e575 100644
--- a/xmake/core/sandbox/modules/io.lua
+++ b/xmake/core/sandbox/modules/io.lua
@@ -30,9 +30,6 @@ local sandbox_io = sandbox_io or {}
local sandbox_io_file = sandbox_io_file or {}
local sandbox_io_filelock = sandbox_io_filelock or {}
local sandbox_io_socket = sandbox_io_socket or {}
-sandbox_io._file = sandbox_io._file or io._file
-sandbox_io._filelock = sandbox_io._filelock or io._filelock
-sandbox_io._socket = sandbox_io._socket or io._socket
sandbox_io.lines = io.lines
-- get file size