Skip to content
This repository has been archived by the owner on Sep 2, 2021. It is now read-only.

Commit

Permalink
Merge pull request #306 from adobe/timburgess/linux_nodejs
Browse files Browse the repository at this point in the history
Implement Node.js for Linux
  • Loading branch information
jasonsanjose committed Aug 22, 2013
2 parents 599191a + 9636c72 commit 486516a
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 22 deletions.
2 changes: 2 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,10 @@ module.exports = function (grunt) {
"src" : [
"lib/**",
"locales/**",
"node-core/**",
"appshell*.png",
"Brackets",
"Brackets-node",
"cef.pak",
"devtools_resources.pak"
],
Expand Down
15 changes: 14 additions & 1 deletion appshell.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,22 @@
{
'destination': '<(PRODUCT_DIR)',
'files': [
'<@(appshell_bundle_resources_linux)',
'<@(appshell_bundle_resources_linux)'
],
},
{
# Copy node executable to the output directory
'destination': '<(PRODUCT_DIR)',
'files': ['deps/node/bin/Brackets-node'],
},
{
# Copy node server files to the output directory
# The '/' at the end of the 'files' directory is very important and magically
# causes 'xcopy' to get used instead of 'copy' for recursive copies.
# This seems to be an undocumented feature of gyp.
'destination': '<(PRODUCT_DIR)',
'files': ['appshell/node-core/'],
},
],
'sources': [
'<@(includes_linux)',
Expand Down
4 changes: 4 additions & 0 deletions appshell/appshell_extensions_gtk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,10 @@ int ConvertLinuxErrorCode(int errorCode, bool isReading)
}
}

int32 CopyFile(ExtensionString src, ExtensionString dest)
{
}

int32 GetPendingFilesToOpen(ExtensionString& files)
{
}
Expand Down
176 changes: 167 additions & 9 deletions appshell/appshell_node_process_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,55 +21,213 @@
*
*/


#include "appshell_node_process.h"
#include "appshell_node_process_internal.h"

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>


#ifndef OS_LINUX
#define OS_LINUX 1
#endif
#include "config.h"

#define BRACKETS_NODE_BUFFER_SIZE 4096
#define MAX_PATH 128

// init mutex
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// write & read references to subprocess
FILE* streamTo;
FILE* streamFrom;

// Threads should hold mutex before using these
static int nodeState = BRACKETS_NODE_NOT_YET_STARTED;
static int nodeStartTime = 0;

// Forward declarations
//DWORD WINAPI NodeThread(LPVOID);
void* nodeThread(void*);
void* nodeReadThread(void*);
void restartNode(bool);

// Creates the thread that starts Node and then monitors the state
// of the node process.
void startNodeProcess() {

pthread_t thread_id;
if (pthread_create(&thread_id, NULL, &nodeThread, NULL) != 0)
nodeState = BRACKETS_NODE_FAILED;
}


// Thread function for the thread that starts the node process
// and monitors that process's state
void* nodeThread(void* unused) {

// get mutex
if (pthread_mutex_lock(&mutex)) {
fprintf(stderr,
"failed to acquire mutex for Node subprocess start: %s\n",
strerror(errno));
return NULL;
}

// TODO nodeStartTime = get time();

char executablePath[MAX_PATH];
char bracketsDirPath[MAX_PATH];
char nodeExecutablePath[MAX_PATH];
char nodecorePath[MAX_PATH];

// get path to Brackets
if (readlink("/proc/self/exe", executablePath, MAX_PATH) == -1) {
fprintf(stderr, "cannot find Brackets path: %s\n", strerror(errno));
pthread_mutex_unlock(&mutex);
return NULL;
}

// strip off trailing executable name
char* lastIndexOf = strrchr(executablePath, '/');
memcpy(bracketsDirPath, executablePath, lastIndexOf - executablePath + 1);

// create node exec and node-core paths
strcpy(nodeExecutablePath, bracketsDirPath);
strcat(nodeExecutablePath, NODE_EXECUTABLE_PATH);
strcpy(nodecorePath, bracketsDirPath);
strcat(nodecorePath, NODE_CORE_PATH);

// create pipes for node process stdin/stdout
int toNode[2];
int fromNode[2];

pipe(toNode);
pipe(fromNode);

// create the Node process
pid_t child_pid = fork();
if (child_pid == 0) { // child (node) process
// close our copy of write end and
// connect read end to stdin
close(toNode[1]);
dup2(toNode[0], STDIN_FILENO);

// close our copy of read end and
// connect write end to stdout
close(fromNode[0]);
dup2(fromNode[1], STDOUT_FILENO);

// run node executable
char* arg_list[] = { nodeExecutablePath, nodecorePath, NULL};
execvp(arg_list[0], arg_list);

fprintf(stderr, "the Node process failed to start: %s\n", strerror(errno));
abort();
}
else { // parent

// close our reference of toNode's read end
// and fromNode's write end
close(toNode[0]);
close(fromNode[1]);

// convert subprocess write & read to FILE objects
streamTo = fdopen(toNode[1], "w");
streamFrom = fdopen(fromNode[0], "r");

nodeState = BRACKETS_NODE_PORT_NOT_YET_SET;

// done launching process so release mutex
if (pthread_mutex_unlock(&mutex)) {
fprintf(stderr,
"failed to release mutex for Node subprocess startup: %s\n",
strerror(errno));
}

// start pipe read thread
pthread_t readthread_id;
if (pthread_create(&readthread_id, NULL, &nodeReadThread, NULL) != 0)
nodeState = BRACKETS_NODE_FAILED;
// ugly - need to think more about what to do if read thread fails

}

return NULL;
}


// Thread function for the thread that reads from the Node pipe
// Reads on anonymous pipes are always blocking (OVERLAPPED reads
// are not possible) So, we need to do this in a separate thread
//

// TODO: This code first reads to a character buffer, and then
// copies it to a std::string. The code could be optimized to avoid
// this double-copy
//DWORD WINAPI NodeReadThread(LPVOID lpParam) {
//}
void* nodeReadThread(void* unused) {

char charBuf[BRACKETS_NODE_BUFFER_SIZE];
std::string strBuf("");
while (fgets(charBuf, BRACKETS_NODE_BUFFER_SIZE, streamFrom) != NULL) {
strBuf.assign(charBuf);
processIncomingData(strBuf);
}
}

// Thread function for the thread that starts the node process
// and monitors that process's state
//DWORD WINAPI NodeThread(LPVOID lpParam) {
//}

// Determines whether the current node process has run long
// enough that a restart is warranted, and initiates the startup
// if so. Can also optionally terminate the running process.
void restartNode(bool terminateCurrentProcess) {

}

// Sends data to the node process. If the write fails completely,
// calls restartNode.
void sendData(const std::string &data) {

if (pthread_mutex_lock(&mutex)) {
fprintf(stderr,
"failed to acquire mutex for write to Node subprocess: %s\n",
strerror(errno));
return;
}

// write to pipe
fprintf(streamTo, "%s", data.c_str());

if (pthread_mutex_unlock(&mutex)) {
fprintf(stderr,
"failed to release mutex for write to Node subprocess: %s\n",
strerror(errno));
}
}

// Thread-safe way to access nodeState variable
// Returns nodeState variable
int getNodeState() {

return nodeState;
}

// Thread-safe way to set nodeState variable
void setNodeState(int newState) {

if (pthread_mutex_lock(&mutex)) {
fprintf(stderr,
"failed to acquire mutex for setting Node state: %s\n",
strerror(errno));
return;
}
nodeState = newState;
if (pthread_mutex_unlock(&mutex)) {
fprintf(stderr,
"failed to release mutex for Node set state: %s\n",
strerror(errno));
}
}
12 changes: 8 additions & 4 deletions appshell/cefclient_gtk.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.

Expand All @@ -13,6 +13,7 @@
#include "include/cef_frame.h"
#include "include/cef_runnable.h"
#include "client_handler.h"
#include "appshell_node_process.h"

static std::string APPICONS[] = {"appshell32.png","appshell48.png","appshell128.png","appshell256.png"};
char szWorkingDir[512]; // The current working directory
Expand All @@ -24,7 +25,7 @@ bool isReallyClosing = false;
// The global ClientHandler reference.
extern CefRefPtr<ClientHandler> g_handler;

//Application startup time
// Application startup time
time_t g_appStartupTime;

void destroy(void) {
Expand Down Expand Up @@ -75,7 +76,7 @@ int GetInitialUrl() {

if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
{
szInitialUrl.append(gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)));
szInitialUrl = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
gtk_widget_destroy (dialog);
return 0;
}
Expand Down Expand Up @@ -247,6 +248,9 @@ int main(int argc, char* argv[]) {
// Install an signal handler so we clean up after ourselves.
signal(SIGINT, TerminationSignalHandler);
signal(SIGTERM, TerminationSignalHandler);

// Start the node server process
startNodeProcess();

CefRunMessageLoop();

Expand All @@ -258,4 +262,4 @@ int main(int argc, char* argv[]) {
CefString AppGetProductVersionString() {
// TODO
return CefString("");
}
}
5 changes: 5 additions & 0 deletions appshell/client_app_gtk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,8 @@ CefString ClientApp::AppGetSupportDirectory()
return home_dir.append("/.Brackets");
}

CefString ClientApp::AppGetDocumentsDirectory()
{
std::string home_dir(getenv("HOME"));
return home_dir;
}
11 changes: 11 additions & 0 deletions appshell/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@
#define NODE_EXECUTABLE_PATH @"/Contents/MacOS/Brackets-node"
#define NODE_CORE_PATH @"/Contents/node-core"

#endif
#ifdef OS_LINUX
// TODO linux preferences
//#define GROUP_NAME @""
//#define APP_NAME @"Brackets"
//#define WINDOW_TITLE APP_NAME

// Path for node resources is in dependencies dir and relative to the location of the appshell executable
#define NODE_EXECUTABLE_PATH "Brackets-node"
#define NODE_CORE_PATH "node-core"

#endif

#define REMOTE_DEBUGGING_PORT 9234
Expand Down
3 changes: 2 additions & 1 deletion installer/linux/build_installer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

# grunt-contrib-copy doesn't preserve permissions
# https://github.com/gruntjs/grunt/issues/615
chmod 755 debian/package-root/opt/brackets/Brackets
chmod 755 debian/package-root/opt/brackets/brackets
chmod 755 debian/package-root/opt/brackets/Brackets
chmod 755 debian/package-root/opt/brackets/Brackets-node
chmod 755 debian/package-root/DEBIAN/prerm
chmod 755 debian/package-root/DEBIAN/postrm
chmod 755 debian/package-root/DEBIAN/postinst
Expand Down
Loading

0 comments on commit 486516a

Please sign in to comment.