Skip to content

Commit

Permalink
Lots of rust cleanup, feet.exe can now be renamed, runtime now unpack…
Browse files Browse the repository at this point in the history
…s to FILE_data from FILE.exe, implemented basic bundle command
  • Loading branch information
ironfroggy committed May 31, 2019
1 parent 697c1f2 commit d7ab081
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 47 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "feet"
version = "0.1.0"
version = "0.3.0"
authors = ["Calvin Spealman <ironfroggy@gmail.com>"]
edition = "2018"

Expand Down
65 changes: 29 additions & 36 deletions feet.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,35 @@
use std::env;
use std::process::{Command, Stdio};
use std::io::{BufRead, BufReader, Error, ErrorKind, Write, stdout};
// use std::io::Error;
use std::path::Path;
use std::io;
use std::fs;
use std::io;
use std::io::{Error, Write, stdout};
use std::path::Path;
use std::process::{Command, Stdio};

use std::io::{Seek, Read};
use zip::result::ZipResult;
use zip::read::{ZipFile, ZipArchive};
// use zip::write::{FileOptions, ZipWriter};
use std::fs::File;

fn browse_zip_archive<T, F, U>(buf: &mut T, browse_func: F) -> ZipResult<Vec<U>>
where T: Read + Seek,
F: Fn(&ZipFile) -> ZipResult<U>
{
let mut archive = ZipArchive::new(buf)?;
(0..archive.len())
.map(|i| archive.by_index(i).and_then(|file| browse_func(&file)))
.collect()
}

fn main() -> Result<(), Error> {
let exec = std::env::current_exe()?;
let exec_path = Path::new(&exec);
let exec_name = exec_path.file_name().unwrap();
let exec_base = exec_path.file_stem().unwrap().to_str().expect("cannot get file stem");
let data_dir = format!("{}_data", exec_base);

if Path::new("./feetmaker.py").exists() {
println!("Do not run feet.exe in its own source directory");
println!("Do not run {:?} in its own source directory", exec_name);
std::process::exit(1);
}

if Path::new("./feet/").exists() {
let mod_exec = fs::metadata("feet.exe")?.modified()?;
let mod_runtime = fs::metadata("feet")?.modified()?;
if Path::new(&data_dir).exists() {
let mod_exec = Path::new(&exec_name).metadata()?.modified()?;
let mod_runtime = Path::new(&data_dir).metadata()?.modified()?;

if (mod_exec > mod_runtime) {
fs::remove_dir_all("./feet/");
if mod_exec > mod_runtime {
fs::remove_dir_all(Path::new(&data_dir))?;
}
}

if !Path::new("./feet/").exists() {
if !Path::new(&data_dir).exists() {
println!("Extracting the Python Feet Runtime... (one-time operation)");
let archive_path = "./feet.exe";
let file = fs::File::open(&archive_path).unwrap();
let file = fs::File::open(&exec_name).unwrap();
let mut archive = zip::ZipArchive::new(file).unwrap();
let total = archive.len();

Expand All @@ -55,7 +44,7 @@ fn main() -> Result<(), Error> {
println!(" {}% ", ((i as f64 / total as f64) * 100.0) as i32);
} else if i != 0 {
print!(".");
stdout().flush();
stdout().flush()?;
}
if let Some(p) = outpath.parent() {
if !p.exists() {
Expand All @@ -76,33 +65,37 @@ fn main() -> Result<(), Error> {
}
}
}
std::fs::rename("feet", &data_dir)?;
println!(" done!");
}

// Next, if there is a requirements file, install that
if Path::new("./requirements.txt").exists() && !Path::new("./Lib/").exists() {
let mut child = Command::new("./feet/cpython/python")
.args(&["./feet/feet.py", "library", "--update"])
let script = &format!("{}/feet.py", data_dir);
println!("Installing requirements... {}", script);
let mut child = Command::new(format!("{}/cpython/python", data_dir))
.args(&[script, "library", "--update"])
.stderr(Stdio::inherit())
.stdout(Stdio::inherit())
.stdin(Stdio::inherit())
.spawn()?;
child.wait();
child.wait().expect("Invoking the Feet runtime script failed.");
}

// Now, runtime is either extracted or already was, so run the commands

let mut args: Vec<String> = env::args().collect();
args.remove(0);

let mut child = Command::new("./feet/cpython/python")
.arg("./feet/feet.py")
println!("{}/feet.py", data_dir);
let mut child = Command::new(format!("{}/cpython/python", data_dir))
.arg(format!("{}/feet.py", data_dir))
.args(args)
.stderr(Stdio::inherit())
.stdout(Stdio::inherit())
.stdin(Stdio::inherit())
.spawn()?;

child.wait();
child.wait().expect("Invoking the Feet runtime script failed.");
Ok(())
}
82 changes: 73 additions & 9 deletions feet/feet.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import argparse
import fnmatch
import glob
import os
import shutil
import subprocess
import sys
import zipfile

# Add path for included third-party packages with the Feet runtime
sys.path.insert(0, os.path.join(sys.executable, 'Lib', 'site-packages'))
Expand Down Expand Up @@ -48,22 +53,63 @@

shell_parser = subparsers.add_parser('shell')

bundle_parser = subparsers.add_parser('bundle')
bundle_parser.add_argument('name', type=str, action='store')
bundle_parser.add_argument('files', type=str, nargs='+')


zip_excludes = [
"*.pyc",
"__pycache__",
]

def add_to_zip(path, dest, compression):
zipf = zipfile.ZipFile(dest, 'a', compression)

for root, _, files in os.walk(path):
for file in files:
src = os.path.join(root, file)
name = os.path.relpath(src, ".")

excluded = False
for pattern in zip_excludes:
if fnmatch.fnmatch(name, pattern):
excluded = True
break
if not excluded:
name = os.path.join("feet", "app", name)
print("...", name)
zipf.write(src, name)

zipf.close()


def main(argv):
feet_exec = argv.pop(0)
args = parser.parse_args(argv)

root = "."
path = os.path.join(root, "main.py")
sys.path.append(os.path.join('.', 'Lib', 'site-packages'))
root = os.path.abspath(os.path.dirname(__file__))
feet_bin = root.split('_data')[0] + '.exe'
assert os.path.exists(feet_bin)
py_bin = os.path.join(root, "cpython", "python.exe")

main = os.path.join(root, "app", "main.py")
if not os.path.exists(main):
main = os.path.join(root, "..", "main.py")
if not os.path.exists(main):
print(HELP)
exit(1)

# sys.path.append(os.path.join(os.path.dirname(main), 'Lib', 'site-packages'))

if args.command == 'run' or not args.command:
if not os.path.exists(path):
print(HELP)
else:
# At this point, we import the main script to "run" it.
# This import statement will block until the Feet app is done.
import main
env = os.environ.copy()
env.update({
'PYTHONPATH': ':'.join((
os.path.join(os.path.dirname(main), 'Lib', 'site-packages'),
)),
})
subprocess.Popen([py_bin, main], env=env)

elif args.command == 'shell':
try:
Expand Down Expand Up @@ -102,6 +148,24 @@ def main(argv):
with open('requirements.txt', 'w') as f:
for _, line in cur_libraries.items():
f.write(f'{line}\n')

elif args.command == 'bundle':
name = args.name
if not name.endswith('.exe'):
name += ".exe"
include = args.files or [main]

shutil.copy(feet_bin, name)

zf = zipfile.ZipFile(name, 'a', zipfile.ZIP_BZIP2)
for pattern in include:
for f in glob.glob(pattern):
print(f, "->", os.path.join("feet", "app", f))
zf.write(f, os.path.join("feet", "app", f))
zf.close()

if os.path.exists("Lib/site-packages/"):
add_to_zip("Lib", name, zipfile.ZIP_BZIP2)


if __name__ == '__main__':
Expand Down

0 comments on commit d7ab081

Please sign in to comment.