Packing your Ruby application into a single executable
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Pan, Minqi
Recent languages like Go compiles a project into a nice executable, why can't good ol' Ruby? We have built an packer for Ruby to do just that. It is 100% open-source, and can produce executables for Windows, macOS and Linux individually. By packing, distributing Ruby apps are made extremely easy, additionally with intellectual property protection. Auto-updating is also made easy, in that the executable only needs to download and replace itself. So, how we did it? How to use it? What goes under the hood? What future will this bring to Ruby? That's what will be unraveled in this talk!
 Good morning. The RubyConf. I'm excited. How are you doing today? My name is Min Chi Pan. I came from China, Beijing, and I'm a hacker of Ruby and C++. I'm also a Node.js collaborator. So I do a lot of open source development. And this is my GitHub page. And those are the links. Oh, I can take off this or not. And today we'll be talking about how to compile your Ruby application into a single exit. So where does the story come from? The other day I was installing the GitHub CI runner on a Windows machine because our project is in C sharp. And I thought, gee, GitHub was written in Ruby. It will be, it seems very hard to install Ruby on Windows. Probably I need to set up the Ruby environment and do some gem installs and stuff. But it turned out that's not the case. They made a single executable for me to download. It's 46 megabyte. After I downloaded that executable, I just executed and installed a service on my CI machine. And well, it works. So why is that? Because they're not writing the CI runner in Ruby. They're writing it in Go. So Go has this nice feature that can build your program into a single product that you're free to distribute. It feels so nice. So in order to solve this distribution problem, people will just need to drop Ruby and use Go. Thanks all for coming. But seriously though, some company does that indeed. Like Heroku, I remember time ago when I installed Heroku COI tours on my machine, on my Windows machine, there was an installer based on Ruby installer that basically installed the Ruby along with the Heroku project code into my machine. But they changed that. The last time I tried Heroku COI is no longer in Ruby. I guess it's also in Go or Node.js or something like that. So I'm kind of jealous about this Go language. I love Ruby. I've been writing Ruby for years. I think we all do love Ruby, right? It's so sweet. I love the language. So about this jealousy, I'm trying to bring that feature into the Ruby world. So what problem are we trying to solve here? Before we, before the Ruby packer, how we distribute our program? Well, something somewhat like this. We have to first, well, assume that the user does not have Ruby installed on their machine. We have to first let them install the Ruby environment and then probably install the REM, then install Ruby. And those all take a long time, a long period of time to complete. I recorded this video in China and they even got worse connection than the US. And that's the only step takes a long time. It's definitely not friendly to the end user. And also finally, after we got the Ruby environment set up on user's machine, you need to let them do some gemming stuff, stuff. And there are a lot of dependencies to download. I think overall, there's not a good experience. And if you consider Windows, there's a disaster you have to. Yeah. So the problem is installation is slow, tons of files to download. And if in China, the Great Wall is somewhat helps. If human makes the thing worse. And sometimes people forgot to use sudo and it's error prone, especially with native modules. And you have to care about the Ruby runtime version. Say if you use the lonely operator that was introduced in Ruby 2.3 and user machine was installed with Ruby 2.1, then your program won't run. And also update. After you distribute your program, you want to keep it up to date. So how do we do that? We have to do the same gemming install thing again. And the user does not even know that you have a new version. And on Windows, you have to install it again. So more version checks. Also comes up to update. As comes up to install. So after I tried this, packing your project into a single executable idea, it turned out to be much better. Look, you can compile your Ruby project into a single file. This is one project that I compiled. It weighs 40 megabytes. And you can just execute it. And you can just slash your program and it runs. And also on Windows, the user no longer have to install those installers anymore. You drop a EXE to them, 33 megabytes. And well, they cannot double click it because it's a command line interface. So you just bring up your CMD and it runs. And this is the best part. Everything has been made so much easier once you pack it into a single executable. It feels so nice that I want to show it to you in the demo. So this is the program that I packed. And when it runs, it will check for a new version of it. And if it finds a new version, it will download a new version and replace itself. Well, the download step happens on the temporary directory, it downloads the file to some temporary location and move that file to this single place to replace itself. The network connection is a little bit slow, so we're probably not doing it. But I was prepared for this, so I have a recording. It was best when I recorded it. Yeah, if ladies are seeing and they say, look, it resumed the execution and you knock out 0.4. And it happens on Windows as well. So you can sort of just put a self-check for versions when your program begins. The operating process is so easy because it's just my file. Just replace yourself. That's the idea. And the tool that I made will help you produce executables like this. You can get the source code of the tool from this address on my GitHub page. It's called Ruby Packer. And I even made a home page for it. It's used to prove the idea that this thing works because this tool itself was written in Ruby and this tool is used to compile Ruby projects. So it must be able to compile itself. So this is an example of the thing that it compiles. It can actually generate those files on three platforms, Windows, Mac, and Linux. You can just download and it works out of the box. So how to use this tool? Well, there are several scenarios. The first scenario is I have this tool installed at this location. If I don't give it any arguments, you will just try to produce a single Ruby executable that is black. It's just the single Ruby executable, interpreter executable. How it looks after it's finished. I have some examples here. This is the final product. And if you execute it, it's just a Ruby executable. So you can print one task once. And there's two. So I wish Ruby was distributed in this way. Actually it's not. Currently Ruby was distributed in source code form. And there's almost no binary distribution. And there is some for the Windows, the Ruby installer. But that's distribution contains so many files. The standard library is so huge. What if we can distribute Ruby in just one single executable? That would be so better. And you must have said Ruby contains so many things. It's just not one single executable there. The contents IRB. Well, IRB is inside this. We'll tell you how it works later. Let's see another scenario where you can use this tool. It's the ordinary scenario where you want to compile your own project. If you have a Ruby project like the one that I got here, you just give the entrance of that project to this tool. And then we'll begin compiling. And after that, you will get an executable for your Ruby project. And that's usually the main scenario that is supposed to be used. And since this Ruby conference is totally on Rails, you cannot forget about Rails. So if you have a Rails project, you can use this tool with the entrance set to being slash Rails. And they will produce a single executable for your Ruby for Rails project. I have already compiled one here, so we can show it. And like this executable, you can see it's 38 megabytes. And I can run it. And they will say you have to add another command. Let's run it as server. Rails always starts with this. Then you go to localhost, 3,000. Yeah, you're on Rails. And the fourth scenario is you can actually pass it to JAN to let it compile the JAN. And also, there are parameters for you to specify auto-update URL to check for new versions so that the auto-updating part is built in. And that's because I made this tour on top of another library called libauto-update. That library basically abstracts away the difference between Windows and Linux and use the socket functions to communicate with the server to check for new versions. So what happens under the hood? How did we make this work? The basic idea is we put your project into something like a mounted disk in your memory along with the Ruby interpreter. And we mounted it at a special location called underscore underscore inflowsio-memfs. You've probably already seen that in the back trace. That is like a virtual file system in memory. And if you, we can do a demo here. I have ported, we can use the other one, the Ruby interpreter, the blank Ruby interpreter that we compiled. And we can give it an entrance. In that entrance you can even reference a file inside the virtual file system. And that's the reason why we can bring up the IRB. And if you look at the load path, that is all in the virtual file system that is actually in memory, part of the executable. So that when you require something, it actually will search inside this virtual memory to give you that. And you can do all kinds of file system operations on it and it all just works. Like if I wonder what is on the load path in this virtual memory, they are being included in the share. So Ruby standard library is actually embedded inside this executable. This is, you know, you used to have this huge library installed on your system, but now it's embedded inside this executable. So all the global level gems are there. This is very similar to what you would install on your machine. And you can even read file out of it. Like file.read give it a virtual path in IRB. Look, that's the IRB that we just involved. So that's the idea. And also the normal files are, you can also read normal files. And they are, like if I want to see what is on the load path in the user's disk. And those are reading from the outside. So it's a combination of a game memory file system and the real file system. The key is if it starts with this thing, it goes into itself. Otherwise it goes to the outside. That's the idea. All kinds of file operations are supported on this virtual file system. Like I can read this file and I can get is, I can get is metadata. At the time that I compile this, you see all those information are stored when I compile this project. So that's the idea. Whatever starts with this path goes to your memory. The others still goes to your disk. So where is your project? We put your project into a special location under this virtual directory called local. If you compile, use this tool to compile your project, you will find your project here at the location. So to wrap it up, you then hard code an entrance. Because it used to run your project via the Ruby interpreter and put your entrance at the RGV bracket one location. But now since we have compiled this into one single executable, we just preset the RGV one to your location so that the user when the user fetches your final executable, it just runs from this location. It just works. So how do we do this? There are so many file APIs. We cannot hack them one by one to add this special functionality, right? It's just not maintainable. It will be subject to change there. 2.5 is coming up. So we did not change on the Ruby level. Yes, we hacked some code on the Ruby level, but in a limited way. Like Ruby IO.C is the source code for a lot of IO operations. We didn't change much. We just included extra header to intercept some of the system calls. And most of the logic is actually inside this small library called Leap Squash. What is Leap Squash? That is actually the core part of it. Well, I want to take this opportunity to thank a few people. This library is not solely made by me. Jay Wali Levski made a library called Squash Fuse. It is a file system implementation on Fuse. And I take a lot of code from him and make it into a library. And also this is my good buddy. He's called Shen Yuan Liu and he helped me write this library as well. So this library basically implements Squash FS. How many of you have heard of this file system? Oh, thank you. Very few people. Squash FS is a compressed read-only file system that was used by Live CD versions, many Linux distributions. Like I used to play with the Ubuntu Live CD. And the Live CD was actually a Squash FS that can be mounted by the kernel when you run it. And also used by some router companies as their firmware. So when we are trying to invent this tool to compile your project, we are looking for a data structure to hold it. And I was looking for different options and I think this Squash FS data structure fits our needs. And it has been there for a while. It's stable. So we used that. And we try the effectiveness of it. If your project weighs over a handwritten megabyte after making it into a Squash FS file, it's been compressed into just like 16 megabytes. And their tools for it is called MK Squash FS. It compiles your project, meanwhile, maybe with the final data structure being able to arbitrarily access, randomly access. So it's a good data structure. The compressing part is important. You don't want the user to have a huge file when you distribute it to them. You cannot let them download something like a multi-handwritten megabyte that is unfriendly as well. It takes a long time to download and update. So we choose one with compressing in it. And that is actually part of the Lang當's kernel since 2009. So we cannot just use that because it's GPL licensed and the code is part of the kernel level. You cannot use it on the application level because the malloc are just K malloc. They're not at the user level. It's the kernel level. So we cannot use the original implementation. So that's when I found the Squash FS. It's a FS implementation. So it's on the user space. And it's MIT licensed. So I took his code and made another library called live squash that removes the fields part out of it. Because we don't want to make any assumptions about users' environment. We do not want to assume that they have fields. We don't need that. So it does not depend on fields. It's just a library implementing the Squash FS access part of it. And it's MIT licensed because the project is also MIT licensed. So that's actually an important point. I'm not going to expand on that because time is running out. And also it compiles on three platforms, Windows, Linux, Mac, so that if you use this, your application is super distributable. And this is the important part. I designed this library to make it mirror the system calls, to mirror a lot of system calls. So it can just come as an in-place replacement for a lot of system calls, the file system ones. And this is another important design. A lot of system calls do not have paths in it. I have to distinguish the path that begins with underscore, underscore, that's better than the screen and path that does not. Some system calls like open, read, write, they actually work on file descriptors. They're just integers. How do I know that if I was open on a virtual file or a real file? So I have to come up with something like a virtual file descriptor. Like if you do open call at the first time, it has a path in it. I will distinguish if this is a real path or a virtual path. If it is virtual path, I issue you a virtual file descriptor. And that was actually generated by this system called I duped the zero file descriptor to make it look like a file descriptor and let it coexist with other FDs issued by the system. But I have it in my record. I have a global data structure called global FD table so that I remember what FD are issued by me and what not. So after you get that FD and made some calls after that, like read, then I will check if this is my FD or the system's FD. If it is my FD, then I will read the data for you from the squash FS. Otherwise, listen to the operating system. So this is how the header, the magic header works. I just intercepted a lot of system calls and redirected to that one. And we spend a lot of time hacking windows as well. Look how ugly are they. We intercepted those calls as well. So it works on windows as well. So what about native extensions? Well we did some compromise on this part. We could make it so perfect as to record all the native extensions at a compile time. But that is really hard. For the moment when you call dr open or load library on windows, we actually extract that file to a temporary location. But it works. For now it is a temporary solution. We extract it to a temporary file and then do the dr open on the temporary file so it still works. And when the process access will delete that file. So native extensions work. So what about Rails? Why does Rails work? This example, Rails is special. It will create some files in your root of your project. But the squash interface is read-only. You cannot write anything to it. Like Rails will write a lot of logs into the root of your project. It will create a lot of temporary files. So how do we solve that? Actually we also intercepted some of those calls to a temporary location. So when the native tries to write files to the squash FS, we actually redirect that call to a temporary location and remember that file and also delete that temporary file when we exit. So if you want to see the files that got created, we have a special environment variable for it, let me create a new folder so that you see and I move the Rails inside of it. And if I run it with Ioworkdir and I say this dot is currently with dr and some things will appear in there. So something already pops up. You can delete that first and run it again with a server. You will see that the log folder and the tmp folder are just created because they're trying to write it to the root of your project. But we redirected that call to a temporary location and that temporary location was specified as the current directory. So the log is there and your config is there. So if you're really going to distribute your Rails app in this way, this is an opportunity for you to spit out the config to make it configurable so that it distributes with the config folder but not your code anymore. That's how it works. So in summary, this is your project and we use Make Squash FS to compile that to yours.squashfs and we compile the two libraries, LibSquash, as the runtime access to the file and LibOut update to give you an update feature and we compile the Ruby runtime. So it's all from the ppap process. So it's a... I have a pen. I have an app wash. Your project leaves the wash. I have a pen. I have a pineapple pen. I have a pineapple pen. It will be runtime. Your project leaves the wash. Pineapple pen. You have a runtime. Your project leaves the wash. Your project leaves the wash. We start to call it link them together. It becomes your.ye and you distribute and enjoy your executable. Thank you very much. Thank you.
 Good morning.  The RubyConf.  I'm excited.  How are you doing today?  My name is Min Chi Pan.  I came from China, Beijing, and I'm a hacker of Ruby and C++.  I'm also a Node.js collaborator.  So I do a lot of open source development.  And this is my GitHub page.  And those are the links.  Oh, I can take off this or not.  And today we'll be talking about how to compile your Ruby application into a single exit.  So where does the story come from?  The other day I was installing the GitHub CI runner on a Windows machine because our  project is in C sharp.  And I thought, gee, GitHub was written in Ruby.  It will be, it seems very hard to install Ruby on Windows.  Probably I need to set up the Ruby environment and do some gem installs and stuff.  But it turned out that's not the case.  They made a single executable for me to download.  It's 46 megabyte.  After I downloaded that executable, I just executed and installed a service on my CI  machine.  And well, it works.  So why is that?  Because they're not writing the CI runner in Ruby.  They're writing it in Go.  So Go has this nice feature that can build your program into a single product that you're  free to distribute.  It feels so nice.  So in order to solve this distribution problem, people will just need to drop Ruby and use  Go.  Thanks all for coming.  But seriously though, some company does that indeed.  Like Heroku, I remember time ago when I installed Heroku COI tours on my machine, on my Windows  machine, there was an installer based on Ruby installer that basically installed the Ruby  along with the Heroku project code into my machine.  But they changed that.  The last time I tried Heroku COI is no longer in Ruby.  I guess it's also in Go or Node.js or something like that.  So I'm kind of jealous about this Go language.  I love Ruby.  I've been writing Ruby for years.  I think we all do love Ruby, right?  It's so sweet.  I love the language.  So about this jealousy, I'm trying to bring that feature into the Ruby world.  So what problem are we trying to solve here?  Before we, before the Ruby packer, how we distribute our program?  Well, something somewhat like this.  We have to first, well, assume that the user does not have Ruby installed on their machine.  We have to first let them install the Ruby environment and then probably install the  REM, then install Ruby.  And those all take a long time, a long period of time to complete.  I recorded this video in China and they even got worse connection than the US.  And that's the only step takes a long time.  It's definitely not friendly to the end user.  And also finally, after we got the Ruby environment set up on user's machine, you need to let  them do some gemming stuff, stuff.  And there are a lot of dependencies to download.  I think overall, there's not a good experience.  And if you consider Windows, there's a disaster you have to.  Yeah.  So the problem is installation is slow, tons of files to download.  And if in China, the Great Wall is somewhat helps.  If human makes the thing worse.  And sometimes people forgot to use sudo and it's error prone, especially with native modules.  And you have to care about the Ruby runtime version.  Say if you use the lonely operator that was introduced in Ruby 2.3 and user machine was  installed with Ruby 2.1, then your program won't run.  And also update.  After you distribute your program, you want to keep it up to date.  So how do we do that?  We have to do the same gemming install thing again.  And the user does not even know that you have a new version.  And on Windows, you have to install it again.  So more version checks.  Also comes up to update.  As comes up to install.  So after I tried this, packing your project into a single executable idea, it turned out  to be much better.  Look, you can compile your Ruby project into a single file.  This is one project that I compiled.  It weighs 40 megabytes.  And you can just execute it.  And you can just slash your program and it runs.  And also on Windows, the user no longer have to install those installers anymore.  You drop a EXE to them, 33 megabytes.  And well, they cannot double click it because it's a command line interface.  So you just bring up your CMD and it runs.  And this is the best part.  Everything has been made so much easier once you pack it into a single executable.  It feels so nice that I want to show it to you in the demo.  So this is the program that I packed.  And when it runs, it will check for a new version of it.  And if it finds a new version, it will download a new version and replace itself.  Well, the download step happens on the temporary directory, it downloads the file to some temporary  location and move that file to this single place to replace itself.  The network connection is a little bit slow, so we're probably not doing it.  But I was prepared for this, so I have a recording.  It was best when I recorded it.  Yeah, if ladies are seeing and they say, look, it resumed the execution and you knock out  0.4.  And it happens on Windows as well.  So you can sort of just put a self-check for versions when your program begins.  The operating process is so easy because it's just my file.  Just replace yourself.  That's the idea.  And the tool that I made will help you produce executables like this.  You can get the source code of the tool from this address on my GitHub page.  It's called Ruby Packer.  And I even made a home page for it.  It's used to prove the idea that this thing works because this tool itself was written  in Ruby and this tool is used to compile Ruby projects.  So it must be able to compile itself.  So this is an example of the thing that it compiles.  It can actually generate those files on three platforms, Windows, Mac, and Linux.  You can just download and it works out of the box.  So how to use this tool?  Well, there are several scenarios.  The first scenario is I have this tool installed at this location.  If I don't give it any arguments, you will just try to produce a single Ruby executable  that is black.  It's just the single Ruby executable, interpreter executable.  How it looks after it's finished.  I have some examples here.  This is the final product.  And if you execute it, it's just a Ruby executable.  So you can print one task once.  And there's two.  So I wish Ruby was distributed in this way.  Actually it's not.  Currently Ruby was distributed in source code form.  And there's almost no binary distribution.  And there is some for the Windows, the Ruby installer.  But that's distribution contains so many files.  The standard library is so huge.  What if we can distribute Ruby in just one single executable?  That would be so better.  And you must have said Ruby contains so many things.  It's just not one single executable there.  The contents IRB.  Well, IRB is inside this.  We'll tell you how it works later.  Let's see another scenario where you can use this tool.  It's the ordinary scenario where you want to compile your own project.  If you have a Ruby project like the one that I got here, you just give the entrance of  that project to this tool.  And then we'll begin compiling.  And after that, you will get an executable for your Ruby project.  And that's usually the main scenario that is supposed to be used.  And since this Ruby conference is totally on Rails, you cannot forget about Rails.  So if you have a Rails project, you can use this tool with the entrance set to being slash  Rails.  And they will produce a single executable for your Ruby for Rails project.  I have already compiled one here, so we can show it.  And like this executable, you can see it's 38 megabytes.  And I can run it.  And they will say you have to add another command.  Let's run it as server.  Rails always starts with this.  Then you go to localhost, 3,000.  Yeah, you're on Rails.  And the fourth scenario is you can actually pass it to JAN to let it compile the JAN.  And also, there are parameters for you to specify auto-update URL to check for new versions  so that the auto-updating part is built in.  And that's because I made this tour on top of another library called libauto-update.  That library basically abstracts away the difference between Windows and Linux and use  the socket functions to communicate with the server to check for new versions.  So what happens under the hood?  How did we make this work?  The basic idea is we put your project into something like a mounted disk in your memory  along with the Ruby interpreter.  And we mounted it at a special location called underscore underscore inflowsio-memfs.  You've probably already seen that in the back trace.  That is like a virtual file system in memory.  And if you, we can do a demo here.  I have ported, we can use the other one, the Ruby interpreter, the blank Ruby interpreter  that we compiled.  And we can give it an entrance.  In that entrance you can even reference a file inside the virtual file system.  And that's the reason why we can bring up the IRB.  And if you look at the load path, that is all in the virtual file system that is actually  in memory, part of the executable.  So that when you require something, it actually will search inside this virtual memory to give  you that.  And you can do all kinds of file system operations on it and it all just works.  Like if I wonder what is on the load path in this virtual memory, they are being included  in the share.  So Ruby standard library is actually embedded inside this executable.  This is, you know, you used to have this huge library installed on your system, but now  it's embedded inside this executable.  So all the global level gems are there.  This is very similar to what you would install on your machine.  And you can even read file out of it.  Like file.read give it a virtual path in IRB.  Look, that's the IRB that we just involved.  So that's the idea.  And also the normal files are, you can also read normal files.  And they are, like if I want to see what is on the load path in the user's disk.  And those are reading from the outside.  So it's a combination of a game memory file system and the real file system.  The key is if it starts with this thing, it goes into itself.  Otherwise it goes to the outside.  That's the idea.  All kinds of file operations are supported on this virtual file system.  Like I can read this file and I can get is, I can get is metadata.  At the time that I compile this, you see all those information are stored when I compile  this project.  So that's the idea.  Whatever starts with this path goes to your memory.  The others still goes to your disk.  So where is your project?  We put your project into a special location under this virtual directory called local.  If you compile, use this tool to compile your project, you will find your project here at  the location.  So to wrap it up, you then hard code an entrance.  Because it used to run your project via the Ruby interpreter and put your entrance at  the RGV bracket one location.  But now since we have compiled this into one single executable, we just preset the RGV  one to your location so that the user when the user fetches your final executable, it  just runs from this location.  It just works.  So how do we do this?  There are so many file APIs.  We cannot hack them one by one to add this special functionality, right?  It's just not maintainable.  It will be subject to change there.  2.5 is coming up.  So we did not change on the Ruby level.  Yes, we hacked some code on the Ruby level, but in a limited way.  Like Ruby IO.C is the source code for a lot of IO operations.  We didn't change much.  We just included extra header to intercept some of the system calls.  And most of the logic is actually inside this small library called Leap Squash.  What is Leap Squash?  That is actually the core part of it.  Well, I want to take this opportunity to thank a few people.  This library is not solely made by me.  Jay Wali Levski made a library called Squash Fuse.  It is a file system implementation on Fuse.  And I take a lot of code from him and make it into a library.  And also this is my good buddy.  He's called Shen Yuan Liu and he helped me write this library as well.  So this library basically implements Squash FS.  How many of you have heard of this file system?  Oh, thank you.  Very few people.  Squash FS is a compressed read-only file system that was used by Live CD versions, many Linux  distributions.  Like I used to play with the Ubuntu Live CD.  And the Live CD was actually a Squash FS that can be mounted by the kernel when you run  it.  And also used by some router companies as their firmware.  So when we are trying to invent this tool to compile your project, we are looking for  a data structure to hold it.  And I was looking for different options and I think this Squash FS data structure fits  our needs.  And it has been there for a while.  It's stable.  So we used that.  And we try the effectiveness of it.  If your project weighs over a handwritten megabyte after making it into a Squash FS file, it's  been compressed into just like 16 megabytes.  And their tools for it is called MK Squash FS.  It compiles your project, meanwhile, maybe with the final data structure being able to  arbitrarily access, randomly access.  So it's a good data structure.  The compressing part is important.  You don't want the user to have a huge file when you distribute it to them.  You cannot let them download something like a multi-handwritten megabyte that is unfriendly  as well.  It takes a long time to download and update.  So we choose one with compressing in it.  And that is actually part of the Lang當's kernel since 2009.  So we cannot just use that because it's GPL licensed and the code is part of the kernel  level.  You cannot use it on the application level because the malloc are just K malloc.  They're not at the user level.  It's the kernel level.  So we cannot use the original implementation.  So that's when I found the Squash FS.  It's a FS implementation.  So it's on the user space.  And it's MIT licensed.  So I took his code and made another library called live squash that removes the fields  part out of it.  Because we don't want to make any assumptions about users' environment.  We do not want to assume that they have fields.  We don't need that.  So it does not depend on fields.  It's just a library implementing the Squash FS access part of it.  And it's MIT licensed because the project is also MIT licensed.  So that's actually an important point.  I'm not going to expand on that because time is running out.  And also it compiles on three platforms, Windows, Linux, Mac, so that if you use this, your  application is super distributable.  And this is the important part.  I designed this library to make it mirror the system calls, to mirror a lot of system  calls.  So it can just come as an in-place replacement for a lot of system calls, the file system  ones.  And this is another important design.  A lot of system calls do not have paths in it.  I have to distinguish the path that begins with underscore, underscore, that's better  than the screen and path that does not.  Some system calls like open, read, write, they actually work on file descriptors.  They're just integers.  How do I know that if I was open on a virtual file or a real file?  So I have to come up with something like a virtual file descriptor.  Like if you do open call at the first time, it has a path in it.  I will distinguish if this is a real path or a virtual path.  If it is virtual path, I issue you a virtual file descriptor.  And that was actually generated by this system called I duped the zero file descriptor to  make it look like a file descriptor and let it coexist with other FDs issued by the system.  But I have it in my record.  I have a global data structure called global FD table so that I remember what FD are issued  by me and what not.  So after you get that FD and made some calls after that, like read, then I will check if  this is my FD or the system's FD.  If it is my FD, then I will read the data for you from the squash FS.  Otherwise, listen to the operating system.  So this is how the header, the magic header works.  I just intercepted a lot of system calls and redirected to that one.  And we spend a lot of time hacking windows as well.  Look how ugly are they.  We intercepted those calls as well.  So it works on windows as well.  So what about native extensions?  Well we did some compromise on this part.  We could make it so perfect as to record all the native extensions at a compile time.  But that is really hard.  For the moment when you call dr open or load library on windows, we actually extract that  file to a temporary location.  But it works.  For now it is a temporary solution.  We extract it to a temporary file and then do the dr open on the temporary file so it  still works.  And when the process access will delete that file.  So native extensions work.  So what about Rails?  Why does Rails work?  This example, Rails is special.  It will create some files in your root of your project.  But the squash interface is read-only.  You cannot write anything to it.  Like Rails will write a lot of logs into the root of your project.  It will create a lot of temporary files.  So how do we solve that?  Actually we also intercepted some of those calls to a temporary location.  So when the native tries to write files to the squash FS, we actually redirect that call  to a temporary location and remember that file and also delete that temporary file when  we exit.  So if you want to see the files that got created, we have a special environment variable for  it, let me create a new folder so that you see and I move the Rails inside of it.  And if I run it with Ioworkdir and I say this dot is currently with dr and some things  will appear in there.  So something already pops up.  You can delete that first and run it again with a server.  You will see that the log folder and the tmp folder are just created because they're trying  to write it to the root of your project.  But we redirected that call to a temporary location and that temporary location was specified  as the current directory.  So the log is there and your config is there.  So if you're really going to distribute your Rails app in this way, this is an opportunity  for you to spit out the config to make it configurable so that it distributes with the config folder  but not your code anymore.  That's how it works.  So in summary, this is your project and we use Make Squash FS to compile that to yours.squashfs  and we compile the two libraries, LibSquash, as the runtime access to the file and LibOut update  to give you an update feature and we compile the Ruby runtime.  So it's all from the ppap process.  So it's a...  I have a pen.  I have an app wash.  Your project leaves the wash.  I have a pen.  I have a pineapple pen.  I have a pineapple pen.  It will be runtime.  Your project leaves the wash.  Pineapple pen.  You have a runtime.  Your project leaves the wash.  Your project leaves the wash.  We start to call it link them together.  It becomes your.ye and you distribute and enjoy your executable.  Thank you very much.  Thank you.
Packing_your_Ruby_0_47.png Packing_your_Ruby_48_287.png Packing_your_Ruby_288_5903.png Packing_your_Ruby_5904_8591.png Packing_your_Ruby_8592_9527.png Packing_your_Ruby_9528_10475.png Packing_your_Ruby_10476_11735.png Packing_your_Ruby_11736_11987.png Packing_your_Ruby_11988_12263.png Packing_your_Ruby_12264_12923.png Packing_your_Ruby_12924_16271.png Packing_your_Ruby_16272_17711.png Packing_your_Ruby_17712_17759.png Packing_your_Ruby_17760_19511.png Packing_your_Ruby_19512_19859.png Packing_your_Ruby_19860_23867.png Packing_your_Ruby_23868_24071.png Packing_your_Ruby_24072_25583.png Packing_your_Ruby_25584_26579.png Packing_your_Ruby_26580_36191.png Packing_your_Ruby_36192_37439.png Packing_your_Ruby_37440_39179.png Packing_your_Ruby_39180_40643.png Packing_your_Ruby_40644_40691.png Packing_your_Ruby_40692_42479.png Packing_your_Ruby_42480_42707.png Packing_your_Ruby_42708_42851.png Packing_your_Ruby_42852_42858.png