mirror of
https://github.com/idris-lang/Idris2.git
synced 2024-11-28 02:23:44 +03:00
[ new ] popen
/pclose
for the NodeJS backend. (#2857)
* implement popen and pclose (to an extent) for NodeJS * bring node020 back into tests. * ah, I see what was being done here. Fix the idris for the test. * fix test's unreachable clause warning * fix expectation * Add note to CHANGELOG * small tweaks to get popen into merge-ready state.
This commit is contained in:
parent
62811c565c
commit
ad12f8335c
@ -30,6 +30,7 @@
|
||||
#### Node.js
|
||||
|
||||
* Generated JavaScript files now include a shebang when using the Node.js backend
|
||||
* NodeJS now supports `popen`/`pclose` for the `Read` mode.
|
||||
|
||||
### Compiler changes
|
||||
|
||||
|
@ -9,8 +9,10 @@ import public System.File.Types
|
||||
%foreign "C:fflush,libc 6"
|
||||
prim__flush : FilePtr -> PrimIO Int
|
||||
%foreign supportC "idris2_popen"
|
||||
supportNode "popen"
|
||||
prim__popen : String -> String -> PrimIO FilePtr
|
||||
%foreign supportC "idris2_pclose"
|
||||
supportNode "pclose"
|
||||
prim__pclose : FilePtr -> PrimIO Int
|
||||
|
||||
||| Force a write of all user-space buffered data for the given `File`.
|
||||
@ -25,6 +27,11 @@ fflush (FHandle f)
|
||||
||| given command-string using the '-c' flag, in a new process. The pipe is
|
||||
||| opened with the given mode.
|
||||
|||
|
||||
||| IMPORTANT: The NodeJS backend only currently supports the Read mode. Also with
|
||||
||| the NodeJS backend, the opened process will finish execution before
|
||||
||| popen returns (it blocks on open) which is different than other
|
||||
||| backends which will block on close.
|
||||
|||
|
||||
||| @ cmd the command to pass to the shell
|
||||
||| @ m the mode the pipe should have
|
||||
export
|
||||
|
@ -30,7 +30,7 @@ prim__readChar : FilePtr -> PrimIO Int
|
||||
prim__writeLine : FilePtr -> String -> PrimIO Int
|
||||
|
||||
%foreign supportC "idris2_eof"
|
||||
"node:lambda:x=>(x.eof?1:0)"
|
||||
"node:lambda:f=>(f.eof?1:0)"
|
||||
prim__eof : FilePtr -> PrimIO Int
|
||||
|
||||
%foreign supportC "idris2_removeFile"
|
||||
|
@ -1,5 +1,5 @@
|
||||
const support_system_file_fs = require('fs')
|
||||
|
||||
const support_system_file_child_process = require('child_process')
|
||||
|
||||
function support_system_file_fileErrno(){
|
||||
const n = process.__lasterr===undefined?0:process.__lasterr.errno || 0
|
||||
@ -62,9 +62,13 @@ function support_system_file_getStr () {
|
||||
return support_system_file_readLine({ fd: 0, buffer: Buffer.alloc(0), name: '<stdin>', eof: false })
|
||||
}
|
||||
|
||||
function support_system_file_parseMode(mode) {
|
||||
return mode.replace('b', '')
|
||||
}
|
||||
|
||||
function support_system_file_openFile (n, m) {
|
||||
try {
|
||||
const fd = support_system_file_fs.openSync(n, m.replace('b', ''))
|
||||
const fd = support_system_file_fs.openSync(n, support_system_file_parseMode(m))
|
||||
return { fd: fd, buffer: Buffer.alloc(0), name: n, eof: false }
|
||||
} catch (e) {
|
||||
process.__lasterr = e
|
||||
@ -91,3 +95,65 @@ function support_system_file_removeFile (filename) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// IMPLEMENTATION NOTE:
|
||||
// If in the future Idris's NodeJS backend supports executing async code, the
|
||||
// far superior and more true-to-C way to implement popen/pclose would be to
|
||||
// spawn in popen (instead of spawnSync) and then in pclose await the processes
|
||||
// completion.
|
||||
//
|
||||
// Note doing the above makes it impossible to support the use-case for popen of
|
||||
// writing to the child process's stdin between popen and pclose.
|
||||
function support_system_file_popen (cmd, m) {
|
||||
const mode = support_system_file_parseMode(m)
|
||||
if (mode != 'r') {
|
||||
process.__lasterr = 'The NodeJS popen FFI only supports opening for reading currently.'
|
||||
return null
|
||||
}
|
||||
|
||||
const tmp_file = require('os').tmpdir() + "/" + require('crypto').randomBytes(15).toString('hex')
|
||||
const write_fd = support_system_file_fs.openSync(
|
||||
tmp_file,
|
||||
'w'
|
||||
)
|
||||
|
||||
var io_setting
|
||||
switch (mode) {
|
||||
case "r":
|
||||
io_setting = ['ignore', write_fd, 2]
|
||||
break
|
||||
case "w", "a":
|
||||
io_setting = [write_fd, 'ignore', 2]
|
||||
break
|
||||
default:
|
||||
process.__lasterr = 'The popen function cannot be used for reading and writing simultaneously.'
|
||||
return null
|
||||
}
|
||||
|
||||
const { status, error } = support_system_file_child_process.spawnSync(
|
||||
cmd,
|
||||
[],
|
||||
{ stdio: io_setting, shell: true }
|
||||
)
|
||||
|
||||
support_system_file_fs.closeSync(write_fd)
|
||||
|
||||
if (error) {
|
||||
process.__lasterr = error
|
||||
return null
|
||||
}
|
||||
|
||||
const read_ptr = support_system_file_openFile(
|
||||
tmp_file,
|
||||
'r'
|
||||
)
|
||||
|
||||
return { ...read_ptr, exit_code: status }
|
||||
}
|
||||
|
||||
function support_system_file_pclose (file_ptr) {
|
||||
const { fd, name, exit_code } = file_ptr
|
||||
support_system_file_fs.closeSync(fd)
|
||||
support_system_file_removeFile(name)
|
||||
return exit_code
|
||||
}
|
||||
|
@ -334,10 +334,10 @@ nodeTests : TestPool
|
||||
nodeTests = MkTestPool "Node backend" [] (Just Node)
|
||||
[ "node001", "node002", "node003", "node004", "node005", "node006"
|
||||
, "node007", "node008", "node009", "node011", "node012", "node015"
|
||||
, "node017", "node018", "node019", "node021", "node022", "node023"
|
||||
, "node024", "node025", "node026", "node027"
|
||||
, "node017", "node018", "node019", "node020", "node021", "node022"
|
||||
, "node023", "node024", "node025", "node026", "node027"
|
||||
, "perf001"
|
||||
-- , "node14", "node020"
|
||||
-- , "node14"
|
||||
, "args"
|
||||
, "bitops"
|
||||
, "casts"
|
||||
|
@ -2,6 +2,7 @@ import System
|
||||
import System.File
|
||||
import System.Info
|
||||
import Data.String
|
||||
import Data.List1
|
||||
|
||||
windowsPath : String -> String
|
||||
windowsPath path =
|
||||
@ -24,6 +25,5 @@ main = do
|
||||
| Left err => printLn err
|
||||
ignore $ pclose fh
|
||||
putStrLn "closed"
|
||||
let [idris2, _] = split ((==) ',') output
|
||||
| _ => printLn "Unexpected result"
|
||||
let (idris2 ::: _) = split ((==) ',') output
|
||||
putStrLn idris2
|
||||
|
@ -1,5 +1,5 @@
|
||||
opened
|
||||
1/1: Building Popen (Popen.idr)
|
||||
Main> opened
|
||||
closed
|
||||
Idris 2
|
||||
1/1: Building Popen (Popen.idr)
|
||||
Main> Main> Bye for now!
|
||||
Main> Bye for now!
|
||||
|
Loading…
Reference in New Issue
Block a user