From 8dfce0a124f1b1349e01392db047db4a6039c923 Mon Sep 17 00:00:00 2001 From: kinuxroot Date: Wed, 23 Nov 2016 14:24:01 +0800 Subject: [PATCH] Add tools and fix bugs. --- .gitignore | 6 + Makefile | 134 +-- deps/kake/.gitignore | 5 - deps/kake/LICENSE | 359 +------ deps/kake/README.md | 41 +- deps/kake/bin/kake | 8 - deps/kake/bin/kake.cmd | 6 - deps/kake/bin/pynewmod | 7 - deps/kake/doc/conf/cpp.txt | 25 - deps/kake/kake | 5 + deps/kake/lib/project.js | 548 +++++++++++ deps/kake/lib/solution.js | 122 +++ deps/kake/lib/template.js | 14 + deps/kake/lib/util/child_process.js | 27 + deps/kake/lib/util/cpp_parser.js | 203 ++++ deps/kake/lib/util/fs.js | 122 +++ deps/kake/lib/util/string.js | 9 + deps/kake/main.js | 64 ++ deps/kake/package.json | 11 + deps/kake/sample/asmexample/Kakefile | 12 - deps/kake/sample/asmexample/src/main/main.asm | 5 - deps/kake/sample/cppexample/Kakefile | 14 - deps/kake/sample/cppexample/src/main/main.cpp | 18 - deps/kake/sample/gmakeexample/Kakefile | 26 - deps/kake/sample/testc/Kakefile | 11 - deps/kake/sample/testc/include/main/hello.h | 1 - deps/kake/sample/testc/src/main/hello/hello.c | 5 - deps/kake/sample/testc/src/main/main.c | 17 - deps/kake/sample/testcpp/Kakefile | 4 - .../sample/testcpp/include/main/hello/hello.h | 11 - .../sample/testcpp/src/main/hello/hello.cpp | 15 - deps/kake/sample/testcpp/src/main/main.cpp | 21 - deps/kake/sample/testsln/Kakefile | 7 - .../sample/testsln/project/testc/Kakefile | 11 - .../project/testc/include/main/hello.h | 1 - .../project/testc/src/main/hello/hello.c | 5 - .../testsln/project/testc/src/main/main.c | 15 - .../sample/testsln/project/testld/Kakefile | 14 - deps/kake/src/backends/__init.py__ | 0 deps/kake/src/backends/gmake/__init.py__ | 0 deps/kake/src/backends/gmake/engine.py | 341 ------- deps/kake/src/main.py | 101 -- deps/kake/src/modules/__init__.py | 0 deps/kake/src/modules/asm/__init__.py | 0 deps/kake/src/modules/cpp/__init__.py | 0 deps/kake/src/modules/cpp/module.py | 242 ----- deps/kake/src/modules/gmake/__init__.py | 0 deps/kake/src/modules/gmake/module.py | 226 ----- deps/kake/src/modules/ld/__init__.py | 0 deps/kake/src/modules/ld/module.py | 180 ---- deps/kake/src/modules/module.py | 156 --- deps/kake/src/modules/solution/__init__.py | 0 deps/kake/src/modules/solution/module.py | 108 --- deps/kake/src/runtest.py | 83 -- deps/kake/src/util.py | 196 ---- deps/kake/templates/project/CppProject.kake | 20 + .../templates/solution/SolutionSample.kake | 9 + deps/kake/templates/solution/deps.kake | 4 + deps/meshy/CMakeLists.txt | 76 -- deps/meshy/Kakefile | 24 - deps/meshy/Makefile | 156 --- deps/meshy/include/DataSink.h | 14 +- deps/meshy/include/IoLoop.h | 24 +- deps/meshy/include/PackageDataSink.h | 52 +- deps/meshy/include/epoll/EPollConnection.h | 3 +- .../epoll/{epollloop.h => EPollLoop.h} | 0 deps/meshy/include/epoll/EPollServer.h | 71 +- deps/meshy/include/epoll/EPollStream.h | 6 +- deps/meshy/include/net.h | 16 +- deps/meshy/include/rest/HttpConnection.h | 2 +- deps/meshy/kake/Kakefile | 12 + deps/meshy/kake/deps.kake | 4 + deps/meshy/kake/meshy/client-sample/Kakefile | 26 + deps/meshy/kake/meshy/meshy/Kakefile | 29 + deps/meshy/kake/meshy/sample/Kakefile | 26 + deps/meshy/src/PackageDataSink.cpp | 3 +- deps/meshy/src/epoll/EPollClient.cpp | 170 ++-- deps/meshy/src/epoll/EPollConnection.cpp | 4 +- deps/meshy/src/epoll/EPollServer.cpp | 218 +++-- deps/meshy/src/epoll/EPollStream.cpp | 2 +- .../epoll/{epollloop.cpp => EpollLoop.cpp} | 6 +- deps/meshy/src/http/HttpConnection.cpp | 4 +- deps/meshy/src/http/HttpServer.cpp | 2 +- deps/meshy/src/sample.cpp | 55 +- deps/meshy/src/utils/logger.cpp | 4 +- .../bin/linux/x64/Release/meshy-client-sample | Bin 0 -> 15382 bytes .../target/bin/linux/x64/Release/meshy-sample | Bin 0 -> 22166 bytes .../target/build/linux/x64/Release/Makefile | 12 + .../build/linux/x64/Release/Makefile.deps} | 0 .../x64/Release/meshy-client-sample/Makefile | 44 + .../linux/x64/Release/meshy-sample/Makefile | 54 ++ .../build/linux/x64/Release/meshy/Makefile | 287 ++++++ .../run/linux/x64/Release/start-client.sh | 6 + .../run/linux/x64/Release/start-server.sh | 7 + docs/introduction.md | 891 +++++++++--------- include/hurricane/base/BlockingQueue.h | 76 ++ include/hurricane/base/DataPackage.h | 113 ++- include/hurricane/base/Library.h | 59 ++ include/hurricane/base/NetAddress.h | 17 +- include/hurricane/base/Serializer.h | 13 + include/hurricane/base/Values.h | 193 +--- include/hurricane/base/Variant.h | 379 +++++++- include/hurricane/base/externc.h | 8 + include/hurricane/bolt/BoltDeclarer.h | 37 +- include/hurricane/collector/OutputCollector.h | 45 +- .../hurricane/collector/OutputDispatcher.h | 99 ++ include/hurricane/collector/OutputQueue.h | 37 + include/hurricane/collector/TaskQueue.h | 32 + include/hurricane/message/Command.h | 24 +- include/hurricane/message/CommandClient.h | 11 +- include/hurricane/message/CommandServer.h | 61 +- include/hurricane/message/Message.h | 54 +- include/hurricane/service/Nimbus.h | 50 +- include/hurricane/service/Supervisor.h | 50 +- include/hurricane/service/SupervisorContext.h | 103 +- include/hurricane/spout/SpoutDeclarer.h | 18 +- include/hurricane/task/BoltExecutor.h | 51 + include/hurricane/task/Executor.h | 22 + include/hurricane/task/SpoutExecutor.h | 38 + include/hurricane/task/TaskDeclarer.h | 8 +- include/hurricane/task/TaskInfo.h | 170 +++- include/hurricane/topology/TopologyLoader.h | 8 +- include/hurricane/util/Socket.h | 4 +- include/hurricane/util/StringUtil.h | 2 + include/redox.hpp | 13 + include/sample/wordcount/HelloWorldSpout.h | 4 +- include/sample/wordcount/SplitSentenceBolt.h | 6 +- include/sample/wordcount/WordCountBolt.h | 4 +- include/sample/wordcount/WordCountTopology.h | 6 +- kake/Hurricane/Kakefile | 3 +- kake/Hurricane/deps.kake | 5 + kake/Hurricane/nimbus/Kakefile | 7 +- kake/Hurricane/supervisor/Kakefile | 7 +- kake/Hurricane/wordcount/Kakefile | 31 + msvc/12/Hurricane/hurricane.sln | 28 - msvc/12/Hurricane/hurricane.suo | Bin 24576 -> 0 bytes msvc/12/Hurricane/hurricane.v12.suo | Bin 92160 -> 0 bytes msvc/12/Hurricane/nimbus.vcxproj | 130 --- msvc/12/Hurricane/nimbus.vcxproj.filters | 210 ----- msvc/12/Hurricane/nimbus.vcxproj.user | 11 - msvc/12/supervisor/supervisor.vcxproj | 140 --- msvc/12/supervisor/supervisor.vcxproj.filters | 258 ----- src/hurricane/base/DataPackage.cpp | 23 +- src/hurricane/base/Library.cpp | 43 + src/hurricane/base/Values.cpp | 21 + src/hurricane/bolt/BoltDeclarer.cpp | 8 +- src/hurricane/collector/OutputCollector.cpp | 10 +- src/hurricane/collector/OutputDispatcher.cpp | 171 ++++ src/hurricane/message/Command.cpp | 6 +- src/hurricane/message/CommandClient.cpp | 14 +- src/hurricane/service/Nimbus.cpp | 510 +++++++++- src/hurricane/service/Supervisor.cpp | 373 +++++++- src/hurricane/service/SupervisorContext.cpp | 136 +-- src/hurricane/spout/SpoutDeclarer.cpp | 8 +- src/hurricane/task/BoltExecutor.cpp | 36 + src/hurricane/task/Executor.cpp | 1 + src/hurricane/task/PathInfo.cpp | 40 + src/hurricane/task/SpoutExecutor.cpp | 43 + src/hurricane/task/TaskInfo.cpp | 27 + src/hurricane/tool/StartNimbus.cpp | 8 +- src/hurricane/tool/StartSupervisor.cpp | 7 +- src/hurricane/topology/TopologyLoader.cpp | 18 +- src/hurricane/util/NetConnector.cpp | 20 +- src/hurricane/util/NetListener.cpp | 8 +- src/hurricane/util/StringUtil.cpp | 21 + src/sample/wordcount/HelloWorldSpout.cpp | 29 +- src/sample/wordcount/SplitSentenceBolt.cpp | 17 +- src/sample/wordcount/WordCountBolt.cpp | 27 +- src/sample/wordcount/WordCountTopology.cpp | 10 +- target/run/avg.js | 30 + target/run/deploy.sh | 3 + target/run/nimbus | Bin 0 -> 435502 bytes target/run/nimbus.properties | 7 +- target/run/run_nimbus.sh | 5 + target/run/run_supervisor.sh | 6 + target/run/supervisor | Bin 0 -> 440097 bytes target/run/supervisor1.properties | 22 +- target/run/supervisor2.properties | 22 +- target/run/supervisor3.properties | 22 +- tools/build.sh | 47 + tools/check_tool.sh | 15 + tools/config.sh | 2 + tools/deps.sh | 4 + tools/download_tool.sh | 10 + tools/install_managers.sh | 8 + tools/nodes.list | 3 + 186 files changed, 5577 insertions(+), 4814 deletions(-) delete mode 100755 deps/kake/.gitignore mode change 100755 => 100644 deps/kake/LICENSE mode change 100755 => 100644 deps/kake/README.md delete mode 100755 deps/kake/bin/kake delete mode 100755 deps/kake/bin/kake.cmd delete mode 100755 deps/kake/bin/pynewmod delete mode 100755 deps/kake/doc/conf/cpp.txt create mode 100755 deps/kake/kake create mode 100644 deps/kake/lib/project.js create mode 100644 deps/kake/lib/solution.js create mode 100644 deps/kake/lib/template.js create mode 100644 deps/kake/lib/util/child_process.js create mode 100644 deps/kake/lib/util/cpp_parser.js create mode 100644 deps/kake/lib/util/fs.js create mode 100644 deps/kake/lib/util/string.js create mode 100644 deps/kake/main.js create mode 100644 deps/kake/package.json delete mode 100755 deps/kake/sample/asmexample/Kakefile delete mode 100755 deps/kake/sample/asmexample/src/main/main.asm delete mode 100755 deps/kake/sample/cppexample/Kakefile delete mode 100755 deps/kake/sample/cppexample/src/main/main.cpp delete mode 100755 deps/kake/sample/gmakeexample/Kakefile delete mode 100755 deps/kake/sample/testc/Kakefile delete mode 100755 deps/kake/sample/testc/include/main/hello.h delete mode 100755 deps/kake/sample/testc/src/main/hello/hello.c delete mode 100755 deps/kake/sample/testc/src/main/main.c delete mode 100755 deps/kake/sample/testcpp/Kakefile delete mode 100755 deps/kake/sample/testcpp/include/main/hello/hello.h delete mode 100755 deps/kake/sample/testcpp/src/main/hello/hello.cpp delete mode 100755 deps/kake/sample/testcpp/src/main/main.cpp delete mode 100755 deps/kake/sample/testsln/Kakefile delete mode 100755 deps/kake/sample/testsln/project/testc/Kakefile delete mode 100755 deps/kake/sample/testsln/project/testc/include/main/hello.h delete mode 100755 deps/kake/sample/testsln/project/testc/src/main/hello/hello.c delete mode 100755 deps/kake/sample/testsln/project/testc/src/main/main.c delete mode 100755 deps/kake/sample/testsln/project/testld/Kakefile delete mode 100755 deps/kake/src/backends/__init.py__ delete mode 100755 deps/kake/src/backends/gmake/__init.py__ delete mode 100755 deps/kake/src/backends/gmake/engine.py delete mode 100755 deps/kake/src/main.py delete mode 100755 deps/kake/src/modules/__init__.py delete mode 100755 deps/kake/src/modules/asm/__init__.py delete mode 100755 deps/kake/src/modules/cpp/__init__.py delete mode 100755 deps/kake/src/modules/cpp/module.py delete mode 100755 deps/kake/src/modules/gmake/__init__.py delete mode 100755 deps/kake/src/modules/gmake/module.py delete mode 100755 deps/kake/src/modules/ld/__init__.py delete mode 100755 deps/kake/src/modules/ld/module.py delete mode 100755 deps/kake/src/modules/module.py delete mode 100755 deps/kake/src/modules/solution/__init__.py delete mode 100755 deps/kake/src/modules/solution/module.py delete mode 100755 deps/kake/src/runtest.py delete mode 100755 deps/kake/src/util.py create mode 100644 deps/kake/templates/project/CppProject.kake create mode 100644 deps/kake/templates/solution/SolutionSample.kake create mode 100644 deps/kake/templates/solution/deps.kake delete mode 100755 deps/meshy/CMakeLists.txt delete mode 100755 deps/meshy/Kakefile delete mode 100755 deps/meshy/Makefile rename deps/meshy/include/epoll/{epollloop.h => EPollLoop.h} (100%) create mode 100644 deps/meshy/kake/Kakefile create mode 100644 deps/meshy/kake/deps.kake create mode 100644 deps/meshy/kake/meshy/client-sample/Kakefile create mode 100644 deps/meshy/kake/meshy/meshy/Kakefile create mode 100644 deps/meshy/kake/meshy/sample/Kakefile rename deps/meshy/src/epoll/{epollloop.cpp => EpollLoop.cpp} (97%) create mode 100755 deps/meshy/target/bin/linux/x64/Release/meshy-client-sample create mode 100755 deps/meshy/target/bin/linux/x64/Release/meshy-sample create mode 100644 deps/meshy/target/build/linux/x64/Release/Makefile rename deps/{kake/Changelog => meshy/target/build/linux/x64/Release/Makefile.deps} (100%) mode change 100755 => 100644 create mode 100644 deps/meshy/target/build/linux/x64/Release/meshy-client-sample/Makefile create mode 100644 deps/meshy/target/build/linux/x64/Release/meshy-sample/Makefile create mode 100644 deps/meshy/target/build/linux/x64/Release/meshy/Makefile create mode 100755 deps/meshy/target/run/linux/x64/Release/start-client.sh create mode 100755 deps/meshy/target/run/linux/x64/Release/start-server.sh create mode 100755 include/hurricane/base/BlockingQueue.h create mode 100644 include/hurricane/base/Library.h create mode 100644 include/hurricane/base/Serializer.h create mode 100644 include/hurricane/base/externc.h create mode 100644 include/hurricane/collector/OutputDispatcher.h create mode 100644 include/hurricane/collector/OutputQueue.h create mode 100644 include/hurricane/collector/TaskQueue.h create mode 100644 include/hurricane/task/BoltExecutor.h create mode 100644 include/hurricane/task/Executor.h create mode 100644 include/hurricane/task/SpoutExecutor.h create mode 100644 include/redox.hpp create mode 100644 kake/Hurricane/wordcount/Kakefile delete mode 100755 msvc/12/Hurricane/hurricane.sln delete mode 100755 msvc/12/Hurricane/hurricane.suo delete mode 100755 msvc/12/Hurricane/hurricane.v12.suo delete mode 100755 msvc/12/Hurricane/nimbus.vcxproj delete mode 100755 msvc/12/Hurricane/nimbus.vcxproj.filters delete mode 100755 msvc/12/Hurricane/nimbus.vcxproj.user delete mode 100755 msvc/12/supervisor/supervisor.vcxproj delete mode 100755 msvc/12/supervisor/supervisor.vcxproj.filters create mode 100644 src/hurricane/base/Library.cpp create mode 100644 src/hurricane/base/Values.cpp create mode 100644 src/hurricane/collector/OutputDispatcher.cpp create mode 100644 src/hurricane/task/BoltExecutor.cpp create mode 100644 src/hurricane/task/Executor.cpp create mode 100644 src/hurricane/task/PathInfo.cpp create mode 100644 src/hurricane/task/SpoutExecutor.cpp create mode 100644 src/hurricane/task/TaskInfo.cpp create mode 100644 target/run/avg.js create mode 100755 target/run/deploy.sh create mode 100755 target/run/nimbus create mode 100755 target/run/run_nimbus.sh create mode 100755 target/run/run_supervisor.sh create mode 100755 target/run/supervisor create mode 100755 tools/build.sh create mode 100755 tools/check_tool.sh create mode 100644 tools/config.sh create mode 100644 tools/deps.sh create mode 100755 tools/download_tool.sh create mode 100755 tools/install_managers.sh create mode 100644 tools/nodes.list diff --git a/.gitignore b/.gitignore index 5eaef8b..72ff104 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,9 @@ msvc/**/Release target/bin/linux/x64/Release/* target/lib/linux/x64/Release/* target/build/linux/x64/Release/* + +*.config +*.creator +*.creator.user +*.files +*.includes diff --git a/Makefile b/Makefile index 2e1a969..0c41cc5 100755 --- a/Makefile +++ b/Makefile @@ -1,129 +1,11 @@ -SRC = src/main -INCLUDE = include/main -TARGET = target -BUILD = $(TARGET)/build -CC = gcc -CXX = g++ - -CXXFLAGS = -std=c++11 -I$(INCLUDE) -DOS_LINUX -g -LDFALGS = -lpthread -Ldeps/meshy/target -lmeshy - -COMMON_OBJECTS = \ - $(BUILD)/DataPackage.o \ - $(BUILD)/OutputCollector.o \ - $(BUILD)/BoltExecutor.o \ - $(BUILD)/BoltOutputCollector.o \ - $(BUILD)/CommandDispatcher.o \ - $(BUILD)/MessageLoop.o \ - $(BUILD)/NimbusCommander.o \ - $(BUILD)/SupervisorCommander.o \ - $(BUILD)/SpoutExecutor.o \ - $(BUILD)/SpoutOutputCollector.o \ - $(BUILD)/SimpleTopology.o \ - $(BUILD)/TopologyBuilder.o \ - -NIMBUS_OBJECTS = $(BUILD)/NimbusLauncher.o - -SUPERVISOR_OBJECTS = $(BUILD)/SupervisorLauncher.o - -all: $(TARGET)/nimbus $(TARGET)/supervisor +all: + cd deps/meshy/target/build/linux/x64/Release;make + cd target/build/linux/x64/Release;make clean: - rm -rf $(TARGET)/* - mkdir $(BUILD) - -$(TARGET)/nimbus: $(COMMON_OBJECTS) $(NIMBUS_OBJECTS) - $(CXX) -o $@ $(COMMON_OBJECTS) $(NIMBUS_OBJECTS) $(LDFALGS) - -$(TARGET)/supervisor: $(COMMON_OBJECTS) $(SUPERVISOR_OBJECTS) - $(CXX) -o $@ $(COMMON_OBJECTS) $(SUPERVISOR_OBJECTS) $(LDFALGS) - -$(BUILD)/DataPackage.o: $(SRC)/hurricane/base/DataPackage.cpp \ - $(INCLUDE)/hurricane/base/DataPackage.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/OutputCollector.o: $(SRC)/hurricane/base/OutputCollector.cpp \ - $(INCLUDE)/hurricane/base/OutputCollector.h \ - $(INCLUDE)/hurricane/topology/ITopology.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/BoltExecutor.o: $(SRC)/hurricane/bolt/BoltExecutor.cpp \ - $(INCLUDE)/hurricane/bolt/BoltExecutor.h \ - $(INCLUDE)/hurricane/bolt/BoltMessage.h \ - $(INCLUDE)/hurricane/message/MessageLoop.h \ - $(INCLUDE)/hurricane/base/OutputCollector.h \ - $(INCLUDE)/hurricane/message/SupervisorCommander.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/BoltOutputCollector.o: $(SRC)/hurricane/bolt/BoltOutputCollector.cpp \ - $(INCLUDE)/hurricane/bolt/BoltOutputCollector.h \ - $(INCLUDE)/hurricane/bolt/BoltExecutor.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/CommandDispatcher.o: $(SRC)/hurricane/message/CommandDispatcher.cpp \ - $(INCLUDE)/hurricane/message/CommandDispatcher.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/MessageLoop.o: $(SRC)/hurricane/message/MessageLoop.cpp \ - $(INCLUDE)/hurricane/message/MessageLoop.h \ - $(INCLUDE)/hurricane/message/Message.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/NimbusCommander.o: $(SRC)/hurricane/message/NimbusCommander.cpp \ - $(INCLUDE)/hurricane/message/SupervisorCommander.h \ - $(INCLUDE)/hurricane/base/ByteArray.h \ - $(INCLUDE)/hurricane/base/DataPackage.h \ - $(INCLUDE)/hurricane/message/Command.h \ - $(INCLUDE)/hurricane/message/NimbusCommander.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/SupervisorCommander.o: $(SRC)/hurricane/message/SupervisorCommander.cpp \ - $(INCLUDE)/hurricane/message/SupervisorCommander.h \ - $(INCLUDE)/hurricane/base/ByteArray.h \ - $(INCLUDE)/hurricane/base/DataPackage.h \ - $(INCLUDE)/hurricane/message/Command.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/SpoutExecutor.o: $(SRC)/hurricane/spout/SpoutExecutor.cpp \ - $(INCLUDE)/hurricane/spout/SpoutExecutor.h \ - $(INCLUDE)/hurricane/base/OutputCollector.h \ - $(INCLUDE)/hurricane/message/SupervisorCommander.h \ - $(INCLUDE)/hurricane/spout/SpoutOutputCollector.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/SpoutOutputCollector.o: $(SRC)/hurricane/spout/SpoutOutputCollector.cpp \ - $(INCLUDE)/SpoutOutputCollector.h \ - $(INCLUDE)/hurricane/spout/SpoutExecutor.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/SimpleTopology.o: $(SRC)/hurricane/topology/SimpleTopology.cpp \ - $(INCLUDE)/hurricane/topology/SimpleTopology.h \ - $(INCLUDE)/hurricane/spout/ISpout.h \ - $(INCLUDE)/hurricane/bolt/IBolt.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/TopologyBuilder.o: $(SRC)/hurricane/topology/TopologyBuilder.cpp \ - $(INCLUDE)/hurricane/topology/TopologyBuilder.h \ - $(INCLUDE)/hurricane/spout/ISpout.h \ - $(INCLUDE)/hurricane/bolt/IBolt.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/NimbusLauncher.o: $(SRC)/hurricane/NimbusLauncher.cpp \ - $(INCLUDE)/hurricane/base/NetAddress.h \ - $(INCLUDE)/hurricane/base/ByteArray.h \ - $(INCLUDE)/hurricane/base/DataPackage.h \ - $(INCLUDE)/hurricane/message/CommandDispatcher.h \ - $(INCLUDE)/hurricane/message/NimbusCommander.h \ - $(INCLUDE)/hurricane/base/Node.h - $(CXX) $(CXXFLAGS) -c -o $@ $< - -$(BUILD)/SupervisorLauncher.o: $(SRC)/hurricane/SupervisorLauncher.cpp \ - $(INCLUDE)/hurricane/base/NetAddress.h \ - $(INCLUDE)/hurricane/base/ByteArray.h \ - $(INCLUDE)/hurricane/base/DataPackage.h \ - $(INCLUDE)/hurricane/base/Value.h \ - $(INCLUDE)/hurricane/base/Variant.h \ - $(INCLUDE)/hurricane/message/SupervisorCommander.h \ - $(INCLUDE)/hurricane/message/CommandDispatcher.h - $(CXX) $(CXXFLAGS) -c -o $@ $< + cd target/build/linux/x64/Release;make clean + cd deps/meshy/target/build/linux/x64/Release;make clean +install: + cd deps/meshy/target/build/linux/x64/Release;make install + cd target/build/linux/x64/Release;make install diff --git a/deps/kake/.gitignore b/deps/kake/.gitignore deleted file mode 100755 index a0c7e99..0000000 --- a/deps/kake/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -test -**/__pycache__ -**/*.swp -sample/*/target -sample/*/project/*/target diff --git a/deps/kake/LICENSE b/deps/kake/LICENSE old mode 100755 new mode 100644 index 36d3f27..a0e961c --- a/deps/kake/LICENSE +++ b/deps/kake/LICENSE @@ -1,339 +1,20 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., [http://fsf.org/] - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {description} - Copyright (C) 2013 kinuxroot - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - {signature of Ty Coon}, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. \ No newline at end of file +The MIT License (MIT) + +Copyright (c) 2016 kinuxroot + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/kake/README.md b/deps/kake/README.md old mode 100755 new mode 100644 index 7d4a51f..5ccb0b0 --- a/deps/kake/README.md +++ b/deps/kake/README.md @@ -1,40 +1 @@ -#Kake Build System - -#### Brief Introduction to Kake -Kake is a building system following the the "convention over configuration" paradigm - -#### Dependencies -Like some of build systems (e.g. scons), Kake build system is written in python3 and using PyYAML library, thus we need to install Python, libyaml and PyYAML. - -Install the following 3rd-party packages if you are using Ubuntu: - - sudo apt-get install libyaml-0-2 libyaml-dev - sudo apt-get install python3 libpython3-dev python3-pip - -Then you may use pip to install PyYAML: - - sudo pip3 install PyYAML - -#### Install Kake -It's very simple to install Kake build system into your environment, you just have to clone it: -Assume you are going to install Kake build system into ~/apps: - - cd ~/apps - git clone https://git.oschina.net/kinuxroot/kake.git - -Then add one line in ~/.bashrc with your favorite text editor: - - export KAKE_HOME=~/apps/kake - -Finally, add the path kake/bin right after PATH: - - export PATH="${PATH}:${KAKE_HOME}/bin" - -#### Verify Installation -If you type in kake in a folder without Kakefile, it will output: - - Project file not exists. - -This indicates that Kake build system is ready to use in your environment, if you want to use it, just simply type: - - kake \ No newline at end of file +#kake diff --git a/deps/kake/bin/kake b/deps/kake/bin/kake deleted file mode 100755 index f5b653a..0000000 --- a/deps/kake/bin/kake +++ /dev/null @@ -1,8 +0,0 @@ -#! /bin/sh - -if [ -z "$KAKE_HOME" ]; then - echo "The environment variable KAKE_HOME is not set!" - echo "Please set KAKE_HOME before run kake" -else - python3 "${KAKE_HOME}/src/main.py" $* -fi diff --git a/deps/kake/bin/kake.cmd b/deps/kake/bin/kake.cmd deleted file mode 100755 index e955f60..0000000 --- a/deps/kake/bin/kake.cmd +++ /dev/null @@ -1,6 +0,0 @@ -@IF "%KAKE_HOME%"=="" ( - @echo The environment variable KAKE_HOME is not set! - @echo Please set KAKE_HOME before run kake -) ELSE ( - @python3 "%KAKE_HOME%\src\main.py" %* -) diff --git a/deps/kake/bin/pynewmod b/deps/kake/bin/pynewmod deleted file mode 100755 index 542b50f..0000000 --- a/deps/kake/bin/pynewmod +++ /dev/null @@ -1,7 +0,0 @@ -#! /bin/sh - -MODULE_PATH="$1" -PACKAGE_FLAG="__init__.py" - -mkdir "${MODULE_PATH}" -touch "${MODULE_PATH}/${PACKAGE_FLAG}" diff --git a/deps/kake/doc/conf/cpp.txt b/deps/kake/doc/conf/cpp.txt deleted file mode 100755 index 9c66456..0000000 --- a/deps/kake/doc/conf/cpp.txt +++ /dev/null @@ -1,25 +0,0 @@ -make: - configuration: - target: - type: - compiler: - c: - src_exts: - cc: - include_paths: - flags: - cpp: - src_exts: - cc: - include_paths: - flags: - inherit_c_include_path: - fpic: - linker: - autolink: - ld: - flags: - library_paths: - libraries: - archiver: - ar: diff --git a/deps/kake/kake b/deps/kake/kake new file mode 100755 index 0000000..2538b39 --- /dev/null +++ b/deps/kake/kake @@ -0,0 +1,5 @@ +#! /bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +node "${DIR}/main.js" $* diff --git a/deps/kake/lib/project.js b/deps/kake/lib/project.js new file mode 100644 index 0000000..2e9649a --- /dev/null +++ b/deps/kake/lib/project.js @@ -0,0 +1,548 @@ +'use strict'; + +let utils = { + fs: require('./util/fs'), + string: require('./util/string'), + child_process: require('./util/child_process') +}; + +let template = require('./template'); +let fs = require('fs'); +let child_process = require('child_process'); + +class Project { + constructor(projectDir, options, solution) { + let self = this; + + this._solution = solution; + this._solutionPath = options.solutionPath; + this._deps = options.deps; + this._basePath = projectDir; + this._completePath = fs.realpathSync(projectDir); + this._kakefile = this._completePath + '/Kakefile'; + let projectConfig = require(this._kakefile); + this._projectConfig = projectConfig; + + this._name = projectConfig.name; + this._version = projectConfig.version; + this._type = projectConfig.type; + this._target = projectConfig.target; + this._targetPath = fs.realpathSync(projectDir + '/' + projectConfig.targetPath); + this._buildPath = this._targetPath + `/build/linux/x64/Release/${this._name}`; + this._binPath = fs.realpathSync(`${this._targetPath}/bin/linux/x64/Release`); + this._libPath = fs.realpathSync(`${this._targetPath}/lib/linux/x64/Release`); + if ( this._target == 'executable' ) { + this._installPath = this._binPath; + } + else { + this._installPath = this._libPath; + } + + // Ignore when the directory exists. + try { + fs.mkdirSync(this._buildPath); + } + catch (e) { + } + this._buildPath = fs.realpathSync(this._buildPath); + + this._compiler = projectConfig.compiler; + this._linker = projectConfig.linker; + this._dependencies = projectConfig.dependencies; + + if ( !this._compiler.exts ) { + this._compiler.exts = ['cpp']; + } + if ( this._target == 'dynamic_library' ) { + if ( this._compiler.cxxflags ) { + this._compiler.cxxflags.push('-fPIC'); + } + else { + this._compiler.cxxflags = ['-fPIC']; + } + } + + this._compiler.src = this._compiler.src.map(srcPath => { + return fs.realpathSync(projectDir + '/' + srcPath); + }); + + let srcDirs = this._compiler.src.filter(srcPath => fs.statSync(srcPath).isDirectory()); + this._compiler.src = this._compiler.src.filter(srcPath => fs.statSync(srcPath).isFile()); + srcDirs.forEach(srcDir => { + let srcFiles = utils.fs.findFiles({ + path: srcDir, + exts: self._compiler.exts + }); + + srcFiles.forEach(srcFile => { + this._compiler.src.push(srcFile); + }); + }); + + if ( this._compiler.useCuda ) { + if ( !this._compiler.nvccExts ) { + this._compiler.nvccExts = ['cu']; + } + + this._compiler.nvccSrc = []; + srcDirs.forEach(srcDir => { + let srcFiles = utils.fs.findFiles({ + path: srcDir, + exts: self._compiler.nvccExts + }); + + srcFiles.forEach(srcFile => { + this._compiler.nvccSrc.push(srcFile); + }); + }); + } + + this._compiler.includePaths = this._compiler.includePaths.map(includePath => { + return fs.realpathSync(projectDir + '/' + includePath); + }); + } + + generateMakefile() { + let configs = this.generateConfig(); + let configLines = Object.keys(configs).map(configKey => { + let configValue = configs[configKey]; + + if ( configValue instanceof Array ) { + configValue = configValue.join(' '); + } + + return `${configKey} := ${configValue}` + }); + + let configContent = configLines.join('\n'); + let configFilePath = `${this._buildPath}/Makefile.config`; + fs.writeFileSync(configFilePath, configContent); + + let depConfigs = this.generateDepConfigs(); + let depConfigsLines = Object.keys(depConfigs).map(configKey => { + let configValue = depConfigs[configKey]; + + if ( configValue instanceof Array ) { + configValue = configValue.join(' '); + } + + return `${configKey} := ${configValue}` + }); + + let depConfigContent = depConfigsLines.join('\n'); + let depConfigFilePath = `${this._buildPath}/../Makefile.deps`; + fs.writeFileSync(depConfigFilePath, depConfigContent); + + let makefileLines = [ + '-include Makefile.config\n', + '-include ../Makefile.deps\n' + ]; + let objectFiles = []; + let dependencyLines = []; + + this._compiler.src.forEach(srcPath => { + let {objectFileName, dependencyLine} = this.generateDependency(srcPath); + + let compilerCommand = this.generateCompilerCommand(srcPath); + dependencyLines.push(dependencyLine); + dependencyLines.push(compilerCommand + '\n\n'); + + objectFiles.push(objectFileName); + }); + + if ( this._compiler.useCuda ) { + this._compiler.nvccSrc.forEach(srcPath => { + let {objectFileName, dependencyLine} = this.generateNvccDependency(srcPath); + + let compilerCommand = this.generateNvccCompilerCommand(srcPath); + dependencyLines.push(dependencyLine); + dependencyLines.push(compilerCommand + '\n\n'); + + objectFiles.push(objectFileName); + }); + } + + makefileLines.push(`OBJS := ${objectFiles.join(' \\\n')}\n`); + + let targetName = this._name; + if ( this._target == 'dynamic_library' ) { + targetName = `lib${targetName}.so`; + } + this._targetName = targetName; + makefileLines.push(`all: ${targetName}\n`); + makefileLines.push(this.generateLinkLine(targetName) + '\n'); + + let binPath = utils.fs.relativePath(this._installPath, this._buildPath); + makefileLines.push(`install: ${binPath}/${targetName}\n`); + makefileLines.push(`${binPath}/${targetName}: ${targetName}`); + makefileLines.push(`\tcp ${targetName} ${binPath}\n`); + + makefileLines.push(`clean:`); + makefileLines.push(`\trm -f ${targetName}`); + makefileLines.push(`\trm -f *.o\n`); + + let makefileContent = `${makefileLines.join('\n')}\n${dependencyLines.join('')}`; + let makefilePath = `${this._buildPath}/Makefile`; + + fs.writeFileSync(makefilePath, makefileContent); + } + + generateLinkLine(targetName) { + let linkLines = [ + `${targetName}: $(OBJS)`, + this.generateLinkerCommand(targetName) + ]; + + return linkLines.join('\n'); + } + + generateLinkerCommand(targetName) { + let command = `\t$(LD) $(OBJS)`; + if ( this._target == 'dynamic_library' ) { + command += ' -shared'; + } + + let libraryPaths = []; + let inputs = []; + + if ( this._dependencies ) { + Object.keys(this._dependencies).forEach(depName => { + let dep = this._deps[depName]; + + if ( !dep ) { + let depProject = this._solution.getProject(depName); + let relLibPath = utils.fs.relativePath(depProject._buildPath, this._buildPath); + + libraryPaths.push(`-L"${relLibPath}"`); + inputs.push(`-l${depProject._name}`); + + return; + } + + if ( dep.scope != 'system' ) { + libraryPaths.push(`-L"$(${depName.toUpperCase()}_LIB)"`); + } + inputs = inputs.concat(dep.inputs.map(input => `-l${input}`)); + }); + } + + let ldflags = ''; + if ( this._linker.ldflags ) { + ldflags = this._linker.ldflags.join(' '); + } + + return `${command} -o ${targetName} ${libraryPaths.join(' ')} ${inputs.join(' ')} ${ldflags}`; + } + + generateConfig() { + let config = { + CXX: this._compiler.cxx, + NVCC: this._compiler.nvcc, + LD: this._linker.ld, + LDFLAGS: this._linker.ldflags, + OS: 'linux', + BITS: '64', + CPU: 'x64', + PLATFORM: '$(OS)/$(CPU)' + }; + + return config; + } + + generateDepConfigs() { + let configs = {}; + + Object.keys(this._deps).forEach(depName => { + let dep = this._deps[depName]; + + if ( dep.scope == 'system' ) { + return; + } + + let includeKey = `${depName}_INCLUDE`.toUpperCase(); + let libKey = `${depName}_LIB`.toUpperCase(); + + let includeValues = dep.include.map(include => { + let includeValue = `${dep.basePath}/${include}`; + if ( !(includeValue.startsWith('/')) ) { + includeValue = utils.fs.relativePath(`${this._solutionPath}/${includeValue}`, this._buildPath); + } + + return `-I"${includeValue}"`; + }); + + let libValue = `${dep.basePath}/${dep.lib}`; + + if ( !(libValue.startsWith('/')) ) { + libValue = utils.fs.relativePath(`${this._solutionPath}/${libValue}`, this._buildPath); + } + + configs[includeKey] = includeValues; + configs[libKey] = libValue; + }); + + return configs; + } + + generateDependency(srcPath) { + console.log(`Resolve dependency: ${srcPath}`); + let cwd = this._buildPath; + let srcRelativePath = utils.fs.relativePath(srcPath, this._buildPath); + let commandArguments = [srcRelativePath, '-c','-MM']; + commandArguments = this.fillCompilerCommandArgs(commandArguments, { + useIncludeVariable: false + }); + + let resolveResult = child_process.execFileSync('g++', commandArguments, { + cwd: cwd, + encoding: 'utf8' + }); + + let fileNameEndIndex = resolveResult.indexOf(':'); + let objectFileName = srcRelativePath; + objectFileName = utils.string.replaceAll(objectFileName, '_', '__'); + objectFileName = utils.string.replaceAll(objectFileName, '-', '--'); + objectFileName = utils.string.replaceAll(objectFileName, '.', '_'); + objectFileName = utils.string.replaceAll(objectFileName, '/', '-'); + objectFileName += '.o'; + + let dependencyLine = objectFileName + + resolveResult + .slice(fileNameEndIndex) + .split('\n') + .filter(line => !(line.trim().startsWith('/'))) + .join('\n'); + + if ( dependencyLine.trim().endsWith('\\') ) { + dependencyLine = dependencyLine.trim(); + dependencyLine = dependencyLine.slice(0, dependencyLine.length - 1) + '\n'; + } + + return { + objectFileName: objectFileName, + dependencyLine: dependencyLine + }; + } + + generateNvccDependency(srcPath) { + console.log(`Resolve dependency: ${srcPath}`); + let cwd = this._buildPath; + let srcRelativePath = utils.fs.relativePath(srcPath, this._buildPath); + let commandArguments = [srcRelativePath, '-c', '--verbose', '--dryrun']; + commandArguments = this.fillNvidiaCompilerCommandArgs(commandArguments, { + useIncludeVariable: false + }); + + let nvccResult = utils.child_process.execFile('nvcc', commandArguments, { + cwd: cwd, + encoding: 'utf8' + }); + + let cbinCommand = nvccResult.stderr.split('\n').find(line => { + return line.startsWith('#$ gcc'); + }); + + let redirectIndex = cbinCommand.indexOf('>'); + cbinCommand = cbinCommand.slice(3, redirectIndex) + ' -MM'; + + let resolveResult = child_process.execSync(cbinCommand, { + cwd: cwd, + encoding: 'utf8' + }); + + let fileNameEndIndex = resolveResult.indexOf(':'); + let objectFileName = srcRelativePath; + objectFileName = utils.string.replaceAll(objectFileName, '_', '__'); + objectFileName = utils.string.replaceAll(objectFileName, '-', '--'); + objectFileName = utils.string.replaceAll(objectFileName, '.', '_'); + objectFileName = utils.string.replaceAll(objectFileName, '/', '-'); + objectFileName += '.o'; + + let dependencyLine = objectFileName + + resolveResult + .slice(fileNameEndIndex) + .split('\n') + .filter(line => !(line.trim().startsWith('/'))) + .join('\n'); + + if ( dependencyLine.trim().endsWith('\\') ) { + dependencyLine = dependencyLine.trim(); + dependencyLine = dependencyLine.slice(0, dependencyLine.length - 1) + '\n'; + } + + return { + objectFileName: objectFileName, + dependencyLine: dependencyLine + }; + } + + generateCompilerCommand(srcPath) { + let srcRelativePath = utils.fs.relativePath(srcPath, this._buildPath); + let commandArguments = ['$(CXX)', srcRelativePath, '-c', '-o', '$@']; + commandArguments = this.fillCompilerCommandArgs(commandArguments, { + useIncludeVariable: true + }); + + return '\t' + commandArguments.join(' '); + } + + generateNvccCompilerCommand(srcPath) { + let srcRelativePath = utils.fs.relativePath(srcPath, this._buildPath); + let commandArguments = ['$(NVCC)', srcRelativePath, '-c', '-o', '$@']; + commandArguments = this.fillNvidiaCompilerCommandArgs(commandArguments, { + useIncludeVariable: true + }); + + return '\t' + commandArguments.join(' '); + } + + fillCompilerCommandArgs(commandArguments, options) { + this._compiler.includePaths.forEach(includePath => { + let incRelPath = utils.fs.relativePath(includePath, this._buildPath); + commandArguments.push(`-I${incRelPath}`); + }); + + if ( this._dependencies ) { + Object.keys(this._dependencies).forEach(depName => { + let dep = this._deps[depName]; + + if ( !dep ) { + let depProject = this._solution.getProject(depName); + depProject._compiler.includePaths.forEach(includePath => { + let incRelPath = utils.fs.relativePath(includePath, this._buildPath); + commandArguments.push(`-I${incRelPath}`); + }); + + return; + } + + if ( dep.scope == 'custom' ) { + if ( options.useIncludeVariable ) { + commandArguments.push(`$(${depName.toUpperCase()}_INCLUDE)`); + } + else { + dep.include.forEach(include => { + let includePath = `${dep.basePath}/${include}`; + if ( !includePath.startsWith('/') ) { + includePath = `${this._solutionPath}/${includePath}`; + let incRelPath = utils.fs.relativePath(includePath, this._buildPath); + commandArguments.push(`-I${incRelPath}`); + } + else { + commandArguments.push(`-I${includePath}`); + } + }); + } + } + }); + } + + if ( this._compiler.std ) { + commandArguments.push(`-std=${this._compiler.std}`); + } + + if ( this._compiler.optimize ) { + commandArguments.push(`-O${this._compiler.optimize}`); + } + else { + commandArguments.push(`-O2`); + } + + commandArguments = commandArguments.concat(this._compiler.cxxflags); + + if ( this._compiler.defines ) { + commandArguments = commandArguments.concat(this._compiler.defines.map(definition => { + return `-D${definition}`; + })); + } + + return commandArguments; + } + + fillNvidiaCompilerCommandArgs(commandArguments, options) { + this._compiler.includePaths.forEach(includePath => { + let incRelPath = utils.fs.relativePath(includePath, this._buildPath); + commandArguments.push(`-I${incRelPath}`); + }); + + if ( this._dependencies ) { + Object.keys(this._dependencies).forEach(depName => { + let dep = this._deps[depName]; + + if ( !dep ) { + let depProject = this._solution.getProject(depName); + depProject._compiler.includePaths.forEach(includePath => { + let incRelPath = utils.fs.relativePath(includePath, this._buildPath); + commandArguments.push(`-I${incRelPath}`); + }); + + return; + } + + if ( dep.scope == 'custom' ) { + if ( options.useIncludeVariable ) { + commandArguments.push(`$(${depName.toUpperCase()}_INCLUDE)`); + } + else { + dep.include.forEach(include => { + let includePath = `${dep.basePath}/${include}`; + if ( !includePath.startsWith('/') ) { + includePath = `${this._solutionPath}/${includePath}`; + let incRelPath = utils.fs.relativePath(includePath, this._buildPath); + commandArguments.push(`-I${incRelPath}`); + } + else { + commandArguments.push(`-I${includePath}`); + } + }); + } + } + }); + } + + if ( this._compiler.std ) { + commandArguments.push(`-std=${this._compiler.std}`); + } + this._compiler.cxxflags.forEach(cxxflag => { + commandArguments.push('-Xcompiler'); + commandArguments.push(cxxflag); + }); + + if ( this._compiler.nvccflags ) { + this._compiler.nvccflags.forEach(nvccflag => { + commandArguments.push(nvccflag); + }); + } + + if ( this._compiler.defines ) { + commandArguments = commandArguments.concat(this._compiler.defines.map(definition => { + return `-D${definition}`; + })); + } + + return commandArguments; + } + +} + +Project.CreateNewProject = function(options) { + let projectName = options.name; + + let projectTemplateContent = template.readTemplateFile('project/CppProject.kake'); + let kakefileContent = projectTemplateContent.replace('CppProject', projectName); + + try { + fs.mkdirSync(projectName); + console.log(`Created directory ${projectName}`); + } + catch (e) { + console.log(`The project dir ${projectName} has been existed. Skip create directory.`); + } + + let kakefilePath = projectName + '/Kakefile'; + fs.writeFileSync(kakefilePath, kakefileContent); + console.log(`Generating ${kakefilePath} ...`); + console.log('Bootstrap finished.'); +} + +module.exports = Project; diff --git a/deps/kake/lib/solution.js b/deps/kake/lib/solution.js new file mode 100644 index 0000000..9b05fc4 --- /dev/null +++ b/deps/kake/lib/solution.js @@ -0,0 +1,122 @@ +'use strict'; + +let fs = require('fs'); +let utils = { + string: require('./util/string') +}; + +let Project = require('./project'); +let template = require('./template'); + +class Solution { + constructor(solutionDir) { + this._basePath = solutionDir; + this._completePath = fs.realpathSync(solutionDir); + this._kakefile = this._completePath + '/Kakefile'; + this._deps = require(this._completePath + '/deps.kake'); + this.processDeps(); + let solutionConfig = require(this._kakefile); + + this._name = solutionConfig.name; + this._version = solutionConfig.version; + this._type = solutionConfig.type; + this._projects = solutionConfig.projects.map(project => { + return new Project(`${this._basePath}/${project}`, { + solutionPath: this._completePath, + deps: this._deps + }, this); + }); + } + + getProject(projectName) { + return this._projects.find(project => { + return project._name == projectName; + }); + } + + processDeps() { + Object.keys(this._deps).forEach(depName => { + let dep = this._deps[depName]; + + if ( !(dep.scope) ) { + dep.scope = 'system'; + } + + if ( !(dep.include) ) { + dep.include = 'include'; + } + + if ( !(dep.lib) ) { + dep.lib = 'lib'; + } + + if ( typeof(dep.include) == 'string' ) { + dep.include = [ dep.include ]; + } + dep.include = dep.include.map(include => { + return utils.string.replaceAll(include, '${Platform}', 'linux/x64'); + }); + dep.lib = utils.string.replaceAll(dep.lib, '${Platform}', 'linux/x64'); + }); + } + + addProject(options) { + let project = Project.CreateNewProject(options); + } + + generateMakefile() { + this._projects.forEach(project => { + project.generateMakefile(); + }); + + let makefileLines = ['all:']; + this._projects.forEach(project => { + makefileLines.push(`\tcd ${project._name};make`); + }); + + makefileLines.push('clean:'); + this._projects.forEach(project => { + makefileLines.push(`\tcd ${project._name};make clean`); + }); + + makefileLines.push('install:'); + this._projects.forEach(project => { + makefileLines.push(`\tcd ${project._name};make install`); + }); + + let makefileContent = makefileLines.join('\n'); + let makefilePath = `${this._projects[0]._targetPath}/build/linux/x64/Release/Makefile`; + console.log(makefilePath); + fs.writeFileSync(makefilePath, makefileContent); + } +} + +Solution.CreateNewSolution = function(options) { + let solutionName = options.name; + + let kakefilePath = solutionName + '/Kakefile'; + console.log(`Generating ${kakefilePath} ...`); + + let solutionTemplateContent = template.readTemplateFile('solution/SolutionSample.kake'); + let kakefileContent = solutionTemplateContent.replace('SampleSolution', solutionName); + + try { + fs.mkdirSync(solutionName); + console.log(`Created directory ${solutionName}`); + } + catch (e) { + console.log(`The solution dir ${solutionName} has been existed. Skip create directory.`); + } + + fs.writeFileSync(kakefilePath, kakefileContent); + + let depsPath = solutionName + '/deps.kake'; + console.log(`Generating ${depsPath} ...`); + + let depsContent = template.readTemplateFile('solution/deps.kake'); + fs.writeFileSync(depsPath, depsContent); + + console.log('Bootstrap finished.'); +} + +module.exports = Solution; diff --git a/deps/kake/lib/template.js b/deps/kake/lib/template.js new file mode 100644 index 0000000..0507fb2 --- /dev/null +++ b/deps/kake/lib/template.js @@ -0,0 +1,14 @@ +'use strict'; + +let fs = require('fs'); + +function readTemplateFile(templateName) { + let templateDir = __dirname + '/../templates/'; + let templatePath = templateDir + templateName; + + return fs.readFileSync(templatePath, 'utf8'); +} + +module.exports = { + readTemplateFile +}; diff --git a/deps/kake/lib/util/child_process.js b/deps/kake/lib/util/child_process.js new file mode 100644 index 0000000..bf10ebd --- /dev/null +++ b/deps/kake/lib/util/child_process.js @@ -0,0 +1,27 @@ +'use strict'; + +let Fiber = require('fibers'); +let child_process = require('child_process'); + +function execFile() { + let args = Array.from(arguments); + let result = {}; + let fiber = Fiber.current; + + args.push((error, stdout, stderr) => { + result.error = error; + result.stdout = stdout; + result.stderr = stderr; + + fiber.run(); + }); + + child_process.execFile.apply(null, args); + Fiber.yield(); + + return result; +} + +module.exports = { + execFile +}; diff --git a/deps/kake/lib/util/cpp_parser.js b/deps/kake/lib/util/cpp_parser.js new file mode 100644 index 0000000..4290d22 --- /dev/null +++ b/deps/kake/lib/util/cpp_parser.js @@ -0,0 +1,203 @@ +'use strict'; + +let fs = require('fs'); + +class CppParser { + parseFile(filePath) { + let fileContent = fs.readFileSync(filePath, 'utf8'); + + let fileMode = this.getFileMode(fileContent); + let lines = fileContent.split(fileMode.lineSeperator); + + this.parseLines(lines); + } + + getFileMode(fileContent) { + if ( fileContent.indexOf('\r\n') ) { + return CppParser.FileMode.DosFile + } + + return CppParser.FileMode.UnixFile; + } + + parseLines(lines) { + let lineCount = lines.length; + + for ( let lineIndex = 0; lineIndex != lineCount; ++ lineIndex ) { + let line = lines[lineIndex]; + + let lineLength = line.length; + console.log(`${lineIndex}: ${line.length}`); + for ( let charIndex = 0; charIndex != lineLength; ++ charIndex ) { + let character = line[charIndex]; + } + } + } +} + +CppParser.FileMode = { + UnixFile: { + type: 'UnixFile', + lineSeperator: '\n' + }, + DosFile: { + type: 'DosFile', + lineSeperator: '\r\n' + } +}; + +CppParser.lexSyntax = [ + { + name: 'string_concat', + start: '##', + pattern: /##/ + }, + { + name: 'make_character', + start: '#@', + pattern: /#@/ + }, + { + name: 'define', + start: '#define', + pattern: /\b#define\b/ + }, + { + name: 'if', + start: '#if', + pattern: /\b#if\b/ + }, + { + name: 'ifdef', + start: '#ifdef', + pattern: /\b#ifdef\b/ + }, + { + name: 'ifndef', + start: '#ifndef', + pattern: /\b#ifndef\b/ + }, + { + name: 'else', + start: '#else', + pattern: /\b#else\b/ + }, + { + name: 'endif', + start: '#endif', + pattern: /\b#endif\b/ + }, + { + name: 'error', + start: '#error', + pattern: /\b#error\b/ + }, + { + name: 'make_string', + start: '#', + pattern: /#/ + }, + { + name: 'string', + start: '"', + pattern: /"[^"]"/ + }, + { + name: 'cpp_comment', + start: '//', + pattern: /\/\/.*$/ + }, + { + name: 'c_comment_start', + start: '/*', + pattern: /\/\*/ + }, + { + name: 'c_comment_end', + start: '*/', + pattern: /\*\// + }, + { + name: 'bracket_open', + start: '(', + pattern: /\(/ + }, + { + name: 'bracket_close', + start: ')', + pattern: /\)/ + }, + { + name: 'comma', + start: ',', + pattern: /,/ + }, + { + name: 'plus', + start: '+', + pattern: /\+/ + }, + { + name: 'minus', + start: '-', + pattern: /-/ + }, + { + name: 'multiply', + start: '*', + pattern: /\*/ + }, + { + name: 'devide', + start: '/', + pattern: /\// + }, + { + name: 'mod', + start: '%', + pattern: /%/ + }, + { + name: 'logic_and', + start: '&&', + pattern: /&&/ + }, + { + name: 'logic_or', + start: '||', + pattern: /||/ + }, + { + name: 'logic_not', + start: '!', + pattern: /!/ + }, + { + name: 'bit_and', + start: '&', + pattern: /&/ + }, + { + name: 'bit_or', + start: '|', + pattern: /|/ + }, + { + name: 'bit_not', + start: '~', + pattern: /~/ + }, + { + name: 'bit_xor', + start: '^', + pattern: /\^/ + }, + { + name: 'word', + start: /[\w]/, + pattern: /[\w][\w\d]*/ + } +]; + +let cppParser = new CppParser(); +cppParser.parseFile('/home/kinuxroot/Projects/authen/AuthenCoreSDK/src/common/caffe-fast-rcnn/caffe/layer.cpp'); diff --git a/deps/kake/lib/util/fs.js b/deps/kake/lib/util/fs.js new file mode 100644 index 0000000..c61cc90 --- /dev/null +++ b/deps/kake/lib/util/fs.js @@ -0,0 +1,122 @@ +'use strict'; + +let fs = require('fs'); + +function relativePath(originPath, basePath) { + let absoluteOriginPath = fs.realpathSync(originPath); + let absoluteBasePath = fs.realpathSync(basePath); + + absoluteOriginPath = replaceAll(absoluteOriginPath, '\\', '/'); + absoluteBasePath = replaceAll(absoluteBasePath, '\\', '/'); + + let originPathParts = absoluteOriginPath.split('/'); + let originFileName = originPathParts[originPathParts.length - 1]; + originPathParts = originPathParts.slice(0, originPathParts.length - 1); + let basePathParts = absoluteBasePath.split('/'); + + let commonPrefixIndex = commonPathPrefix(originPathParts, basePathParts); + let backTimes = basePathParts.length - commonPrefixIndex; + let backPathParts = makeBackPathParts(backTimes); + let remainingPathParts = originPathParts.slice(commonPrefixIndex); + + let finalPathParts = backPathParts.concat(remainingPathParts); + finalPathParts.push(originFileName); + + let finalPath = finalPathParts.join('/'); + + return finalPath; +} + +function findFiles(options) { + let foundFiles = []; + + let rootDir = fs.realpathSync(options.path); + let exts = options.exts; + + let subItems = fs.readdirSync(rootDir); + let subDirs = []; + + subItems.forEach(subItem => { + let subItemPath = rootDir + '/' + subItem; + + let itemStatus = fs.statSync(subItemPath); + if ( itemStatus.isFile() && endsWithExts(subItem, exts) ) { + foundFiles.push(subItemPath); + } + else if ( itemStatus.isDirectory() ) { + subDirs.push(subItemPath); + } + }); + + subDirs.forEach(subDir => { + foundFiles = foundFiles.concat(findFiles({ + path: subDir, + exts: exts + })); + }); + + return foundFiles; +} + +function endsWithExts(fileName, exts) { + exts = exts.map(ext => `.${ext}`); + return exts.reduce((prev, ext) => { + if ( !prev ) { + return fileName.endsWith(ext); + } + + return true; + }, false); +} + +function commonPathPrefix(pathParts1, pathParts2) { + let minLength = min(pathParts1.length, pathParts2.length); + + for ( let partIndex = 0; partIndex < minLength; partIndex ++ ) { + if ( pathParts1[partIndex] != pathParts2[partIndex] ) { + return partIndex; + } + } + + return minLength; +} + +function makeBackPathParts(backTimes) { + let parts = []; + for ( let backTime = 0; backTime < backTimes; backTime ++ ) { + parts.push('..'); + } + + return parts; +} + +function replaceAll(str, cond, rep) { + let origin = str; + while ( true ) { + let result = origin.replace(cond, rep); + if ( result == origin ) { + return result; + } + + origin = result; + } +} + +function min(a, b) { + if ( a < b ) { + return a; + } + + return b; +} + +module.exports = { + relativePath, + findFiles +}; + +//console.log( +//findFiles({ +// path: '../src/detector/caffe-fast-rcnn', +// exts: ['cpp', 'c'] +//})); diff --git a/deps/kake/lib/util/string.js b/deps/kake/lib/util/string.js new file mode 100644 index 0000000..3639577 --- /dev/null +++ b/deps/kake/lib/util/string.js @@ -0,0 +1,9 @@ +'use strict'; + +function replaceAll(str, cond, rep) { + return str.split(cond).join(rep); +} + +module.exports = { + replaceAll +}; diff --git a/deps/kake/main.js b/deps/kake/main.js new file mode 100644 index 0000000..d08204c --- /dev/null +++ b/deps/kake/main.js @@ -0,0 +1,64 @@ +'use strict' + +let Fiber = require('fibers'); +let Solution = require('./lib/solution'); + +function main() { + if ( process.argv.length < 3 ) { + console.log('Usage: kake '); + process.exit(-1); + } + + let action = process.argv[2]; + + if ( action == 'bootstrap' ) { + if ( process.argv.length < 5 ) { + console.log('Usage: kake bootstrap '); + process.exit(-1); + } + + let projectType = process.argv[3]; + let solutionName = process.argv[4]; + + if ( projectType != 'solution' ) { + console.log('Kake only support bootstrap solution now'); + process.exit(-1); + } + + let solution = Solution.CreateNewSolution({ + name: solutionName + }); + } + else if ( action == 'generate' ) { + let solutionDir = '.'; + if ( process.argv.length > 3 ) { + solutionDir = process.argv[3]; + } + + let solution = new Solution(solutionDir); + + solution.generateMakefile(); + } + else if ( action == 'execute' ) { + let solution = new Solution('.'); + + let command = process.argv[3]; + if ( command == 'add' ) { + let itemType = process.argv[4]; + if ( itemType == 'project' ) { + let projectName = process.argv[5]; + solution.addProject({ + name: projectName + }); + } + } + } + else if ( action == 'build' ) { + let solution = new Solution('.'); + } + + + process.exit(); +} + +Fiber(main).run(); diff --git a/deps/kake/package.json b/deps/kake/package.json new file mode 100644 index 0000000..df1b42b --- /dev/null +++ b/deps/kake/package.json @@ -0,0 +1,11 @@ +{ + "name": "kake", + "description": "Makefile generator", + "version": "0.0.1", + "private": true, + "dependencies": { + "fibers": "latest", + "pixl-xml": "latest", + "uuid": "latest" + } +} diff --git a/deps/kake/sample/asmexample/Kakefile b/deps/kake/sample/asmexample/Kakefile deleted file mode 100755 index e709b97..0000000 --- a/deps/kake/sample/asmexample/Kakefile +++ /dev/null @@ -1,12 +0,0 @@ -project: - name: example - version: 0.0.1 - type: cpp - -make: - configuration: - compiler: - asm: - flags: "-f elf64" - linker: - ld: gcc diff --git a/deps/kake/sample/asmexample/src/main/main.asm b/deps/kake/sample/asmexample/src/main/main.asm deleted file mode 100755 index 7e04bad..0000000 --- a/deps/kake/sample/asmexample/src/main/main.asm +++ /dev/null @@ -1,5 +0,0 @@ -global main - -[section .text] -main: - mov eax, 0 diff --git a/deps/kake/sample/cppexample/Kakefile b/deps/kake/sample/cppexample/Kakefile deleted file mode 100755 index 96a2f0e..0000000 --- a/deps/kake/sample/cppexample/Kakefile +++ /dev/null @@ -1,14 +0,0 @@ -project: - name: example - version: 0.0.1 - type: cpp - -make: - configuration: - compiler: - c: - include_paths: [ "/home/stormdev/Application/boost_1_57_0" ] - linker: - ld: g++ - library_paths: [ "/home/stormdev/Application/boost_1_57_0/stage/lib" ] - libraries: [ "boost_regex" ] diff --git a/deps/kake/sample/cppexample/src/main/main.cpp b/deps/kake/sample/cppexample/src/main/main.cpp deleted file mode 100755 index 9733734..0000000 --- a/deps/kake/sample/cppexample/src/main/main.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include -#include - -int main(){ - std::string line; - boost::regex pat("^Subject: (Re: |Aw: )*(.*)"); - - while ( std::cin ) { - std::getline(std::cin, line); - boost::smatch matches; - if ( boost::regex_match(line, matches, pat) ) { - std::cout << matches[2] << std::endl; - } - } - - return 0; -} diff --git a/deps/kake/sample/gmakeexample/Kakefile b/deps/kake/sample/gmakeexample/Kakefile deleted file mode 100755 index 4817a5c..0000000 --- a/deps/kake/sample/gmakeexample/Kakefile +++ /dev/null @@ -1,26 +0,0 @@ -project: - name: example - version: 0.0.1 - type: gmake - -make: - rules: - all: - dependencies: - - example - example: - dependencies: - - main.o - - hello.o - actions: - - { command: gcc, args: [ "-o", "example", "main.o", "hello.o" ] } - main.o: - dependencies: - - main.c - actions: - - { command: gcc, args: [ "-c", "-o", "main.o", "main.c"] } - hello.o: - dependencies: - - hello.c - actions: - - { command: gcc, args: [ "-c", "-o", "hello.o", "hello.c"] } diff --git a/deps/kake/sample/testc/Kakefile b/deps/kake/sample/testc/Kakefile deleted file mode 100755 index eae7327..0000000 --- a/deps/kake/sample/testc/Kakefile +++ /dev/null @@ -1,11 +0,0 @@ -project: - name: testc - version: 0.0.1 - type: c - -make: - configuration: - compiler: - cflags: -std=c99 - linker: - autolink: true diff --git a/deps/kake/sample/testc/include/main/hello.h b/deps/kake/sample/testc/include/main/hello.h deleted file mode 100755 index 9bb085a..0000000 --- a/deps/kake/sample/testc/include/main/hello.h +++ /dev/null @@ -1 +0,0 @@ -void hello(); diff --git a/deps/kake/sample/testc/src/main/hello/hello.c b/deps/kake/sample/testc/src/main/hello/hello.c deleted file mode 100755 index a75545f..0000000 --- a/deps/kake/sample/testc/src/main/hello/hello.c +++ /dev/null @@ -1,5 +0,0 @@ -#include - -void hello(){ - printf("hello,world\n"); -} diff --git a/deps/kake/sample/testc/src/main/main.c b/deps/kake/sample/testc/src/main/main.c deleted file mode 100755 index 42b96db..0000000 --- a/deps/kake/sample/testc/src/main/main.c +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include -#include - -int main(int argc, char** argv){ - hello(); - FILE* f = fopen(argv[1], "r"); - if ( f ) { - char str[80]; - memset(str, '\0', 80); - - fgets(str, 80, f); - puts(str); - } - - return 0; -} diff --git a/deps/kake/sample/testcpp/Kakefile b/deps/kake/sample/testcpp/Kakefile deleted file mode 100755 index 20dbcae..0000000 --- a/deps/kake/sample/testcpp/Kakefile +++ /dev/null @@ -1,4 +0,0 @@ -project: - name: testcpp - version: 0.0.1 - type: cpp diff --git a/deps/kake/sample/testcpp/include/main/hello/hello.h b/deps/kake/sample/testcpp/include/main/hello/hello.h deleted file mode 100755 index 895d897..0000000 --- a/deps/kake/sample/testcpp/include/main/hello/hello.h +++ /dev/null @@ -1,11 +0,0 @@ -/** @file: hello.h - * @brief: Hello header file - * @date: 2015年03月06日 18时27分21秒 - * @author: kinuxroot, kinuxroot@163.com - * @group: LessOS Development Group - * @version: 1.0 - * @note: - * - */ - -void hello(); diff --git a/deps/kake/sample/testcpp/src/main/hello/hello.cpp b/deps/kake/sample/testcpp/src/main/hello/hello.cpp deleted file mode 100755 index dddbc87..0000000 --- a/deps/kake/sample/testcpp/src/main/hello/hello.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/** @file: hello.cpp - * @brief: Hello implementation - * @date: 2015年03月06日 18时26分49秒 - * @author: kinuxroot, kinuxroot@163.com - * @group: LessOS Development Group - * @version: 1.0 - * @note: - * - */ - -#include - -void hello(){ - std::cout << "hello" << std::endl; -} diff --git a/deps/kake/sample/testcpp/src/main/main.cpp b/deps/kake/sample/testcpp/src/main/main.cpp deleted file mode 100755 index 44a78f1..0000000 --- a/deps/kake/sample/testcpp/src/main/main.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/** @file: main.cpp - * @brief: Main cpp file - * @date: 2015年03月06日 18时20分50秒 - * @author: kinuxroot, kinuxroot@163.com - * @group: LessOS Development Group - * @version: 1.0 - * @note: - * - */ - -#include "hello/hello.h" -#include - -int main(int argc, char** argv){ - hello(); - if ( argc > 1 ) { - std::cout << argv[1] << std::endl; - } - - return 0; -} diff --git a/deps/kake/sample/testsln/Kakefile b/deps/kake/sample/testsln/Kakefile deleted file mode 100755 index 1f3a17b..0000000 --- a/deps/kake/sample/testsln/Kakefile +++ /dev/null @@ -1,7 +0,0 @@ -project: - name: testsln - version: 0.0.1 - type: solution - -run: - default: testld diff --git a/deps/kake/sample/testsln/project/testc/Kakefile b/deps/kake/sample/testsln/project/testc/Kakefile deleted file mode 100755 index ed3d023..0000000 --- a/deps/kake/sample/testsln/project/testc/Kakefile +++ /dev/null @@ -1,11 +0,0 @@ -project: - name: testc - version: 0.0.1 - type: cpp - -make: - configuration: - compiler: - cflags: -std=c99 - linker: - autolink: false diff --git a/deps/kake/sample/testsln/project/testc/include/main/hello.h b/deps/kake/sample/testsln/project/testc/include/main/hello.h deleted file mode 100755 index 9bb085a..0000000 --- a/deps/kake/sample/testsln/project/testc/include/main/hello.h +++ /dev/null @@ -1 +0,0 @@ -void hello(); diff --git a/deps/kake/sample/testsln/project/testc/src/main/hello/hello.c b/deps/kake/sample/testsln/project/testc/src/main/hello/hello.c deleted file mode 100755 index a75545f..0000000 --- a/deps/kake/sample/testsln/project/testc/src/main/hello/hello.c +++ /dev/null @@ -1,5 +0,0 @@ -#include - -void hello(){ - printf("hello,world\n"); -} diff --git a/deps/kake/sample/testsln/project/testc/src/main/main.c b/deps/kake/sample/testsln/project/testc/src/main/main.c deleted file mode 100755 index e008c72..0000000 --- a/deps/kake/sample/testsln/project/testc/src/main/main.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include -#include - -int main(){ - hello(); - FILE* f = fopen("1.txt", "r"); - char str[80]; - memset(str, '\0', 80); - - fgets(str, 80, f); - puts(str); - - return 0; -} diff --git a/deps/kake/sample/testsln/project/testld/Kakefile b/deps/kake/sample/testsln/project/testld/Kakefile deleted file mode 100755 index f99b97a..0000000 --- a/deps/kake/sample/testsln/project/testld/Kakefile +++ /dev/null @@ -1,14 +0,0 @@ -project: - name: testld - version: 0.0.1 - type: ld - -dependencies: - - { name: testc, source: { type: solution } } - -make: - configuration: - target: - type: executable - linker: - include: [ testc ] diff --git a/deps/kake/src/backends/__init.py__ b/deps/kake/src/backends/__init.py__ deleted file mode 100755 index e69de29..0000000 diff --git a/deps/kake/src/backends/gmake/__init.py__ b/deps/kake/src/backends/gmake/__init.py__ deleted file mode 100755 index e69de29..0000000 diff --git a/deps/kake/src/backends/gmake/engine.py b/deps/kake/src/backends/gmake/engine.py deleted file mode 100755 index 73943a9..0000000 --- a/deps/kake/src/backends/gmake/engine.py +++ /dev/null @@ -1,341 +0,0 @@ -from util import FileUtil, Path, PathUtil -import os - -class Document(object): - def __init__(self, documentPath): - self.path = documentPath - self.rules = [] - self.subDocuments = [] - pass - - def __str__(self): - return "GNU Makefile Document Object" - - def getPath(self): - return self.path - - def addRule(self, rule): - self.rules.append(rule) - - def addSubDocument(self, subDocument): - self.subDocuments.append(subDocument) - - def writeToFile(self): - makefileContent = self.toText() - makefile = FileUtil.openFile(self.path) - makefile.writelines(makefileContent) - - for subDocument in self.subDocuments: - subDocument.writeToFile() - - makefile.close() - - def toText(self): - makefileContent = [] - for rule in self.rules: - makefileContent.append(rule.toText()) - makefileContent.append('') - - for subDocument in self.subDocuments: - includeInstruction = "-include %(path)s" % { "path": subDocument.getPath() } - makefileContent.append(includeInstruction) - - return '\n'.join(makefileContent) - -class Rule(object): - def __init__(self): - self.prebuildActions = [] - self.buildActions = [] - self.postbuildActions = [] - self.target = "" - pass - - def __str__(self): - return "GNU Makefile Rule Object" - - def addPrebuildAction(self, action): - self.prebuildActions.append(action) - - def addBuildAction(self, action): - self.buildActions.append(action) - - def addPostbuildAction(self, action): - self.postbuildActions.append(action) - - def setTarget(self, target): - self.target = target - - def getTarget(self): - return self.target - - def toText(self): - textContent = [] - textContent.append(self.getTargetLine()) - - for prebuildAction in self.prebuildActions: - textContent.append('\t' +prebuildAction.toText()) - - for buildAction in self.buildActions: - textContent.append('\t' + buildAction.toText()) - - for postbuildAction in self.postbuildActions: - textContent.append('\t' + postbuildAction.toText()) - - return '\n'.join(textContent) - - def getTargetLine(self): - raise NotImplementedError() - -class Action(object): - def __init__(self): - pass - - def __str__(self): - return "GNU Makefile Action Object" - - def toText(self): - raise NotImplementedError() - -class SimpleRule(Rule): - def __init__(self, target, prerequisites): - Rule.__init__(self) - self.setTarget(target) - self.setPrerequisites(prerequisites) - pass - - def setPrerequisites(self, prerequisites): - self.prerequisites = prerequisites - - def getPrerequisites(self): - return self.prerequisites - - def getTargetLine(self): - shellStrings = [] - for prerequisite in self.prerequisites: - shellStrings.append(prerequisite.shellString()) - - return '%(target)s: %(prerequisites)s' % { - 'target': self.getTarget(), - 'prerequisites': ' '.join(shellStrings) - } - -class GCCCompileRule(Rule): - def __init__(self, sourceFilePath, includePaths, sourceDirPath, objectDirPath, makeConf): - Rule.__init__(self) - - relPath = sourceFilePath.getRelevantPath(sourceDirPath) - dirName = relPath.getDirName() - baseName = relPath.getBaseName() - - objectSubDirPath = objectDirPath.join(dirName) - objectFilePath = objectSubDirPath.join(PathUtil.getPrefix(baseName)) - if sourceFilePath.getExt() not in makeConf['asm_src_exts']: - objectFilePath.appendExt('o') - if sourceFilePath.getExt() in makeConf['asm_src_exts'] and makeConf['asm_with_object_ext']: - objectFilePath.appendExt('o') - self.setTarget(objectFilePath.shellString()) - - self.sourceFilePath = sourceFilePath - self.includePaths = includePaths - self.objectSubDirPath = objectSubDirPath - - if self.sourceFilePath.getExt() in makeConf['c_src_exts']: - self.includePaths.extend(PathUtil.toPathList(makeConf['c_include_paths'])) - elif self.sourceFilePath.getExt() in makeConf['cxx_src_exts']: - if makeConf['cxx_using_c_include_paths']: - self.includePaths.extend(PathUtil.toPathList(makeConf['c_include_paths'])) - self.includePaths.extend(PathUtil.toPathList(makeConf['cxx_include_paths'])) - elif self.sourceFilePath.getExt() in makeConf['asm_src_exts']: - self.includePaths.extend(PathUtil.toPathList(makeConf['asm_include_paths'])) - - self.targetLine = self.generateTargetLine(makeConf) - - makeDirectoryAction = MakeDirectoryAction(objectSubDirPath) - compileAction = GCCCompileAction(objectFilePath, sourceFilePath, includePaths, makeConf) - self.addBuildAction(makeDirectoryAction) - self.addBuildAction(compileAction) - - def generateTargetLine(self, makeConf): - includePathShellStrings = [] - for includePath in self.includePaths: - includePathShellStrings.append('-I' + includePath.shellString() + '/') - - dependChecker = '' - compilerFlags = '' - if self.sourceFilePath.getExt() in makeConf['c_src_exts']: - dependChecker = makeConf['cc'] - compilerFlags = makeConf['cflags'] - elif self.sourceFilePath.getExt() in makeConf['cxx_src_exts']: - dependChecker = makeConf['cxxc'] - compilerFlags = makeConf['cxxflags'] - elif self.sourceFilePath.getExt() in makeConf['asm_src_exts']: - dependChecker = makeConf['as'] - compilerFlags = makeConf['asflags'] - - dependOption = '' - if self.sourceFilePath.getExt() in makeConf['c_src_exts']: - dependOption = '-MM' - elif self.sourceFilePath.getExt() in makeConf['cxx_src_exts']: - dependOption = '-MM' - elif self.sourceFilePath.getExt() in makeConf['asm_src_exts']: - dependOption = '-M' - - gccCheckCommand = ('%(depend_checker)s %(compiler_flags)s %(depend_option)s %(filename)s %(include_main_path)s' % - { - 'depend_checker': dependChecker, - 'compiler_flags': compilerFlags, - 'depend_option': dependOption, - 'filename': self.sourceFilePath, - 'include_main_path': ' '.join(includePathShellStrings) - }) - gccCheckResult = os.popen(gccCheckCommand).readlines() - - targetLineContent = [] - if len(gccCheckResult) > 0: - if self.sourceFilePath.getExt() in makeConf['c_src_exts'] or self.sourceFilePath.getExt() in makeConf['cxx_src_exts']: - gccCheckResult[0] = str(self.objectSubDirPath.join(gccCheckResult[0])) - elif self.sourceFilePath.getExt() in makeConf['asm_src_exts']: - gccCheckResult[0] = str(self.getTarget() + gccCheckResult[0]) - - for line in gccCheckResult: - outputLine = str(line).rstrip() - if len(outputLine) == 0 : - continue - if outputLine[-1] == '/': - outputLine = outputLine[:-1] + '\\' - targetLineContent.append(outputLine) - - return '\n'.join(targetLineContent) - - def getTargetLine(self): - return self.targetLine - -class GCCCompileAction(Action): - def __init__(self, objectFilePath, sourceFilePath, includePaths, makeConf): - Action.__init__(self) - self.objectFilePath = objectFilePath - self.sourceFilePath = sourceFilePath - self.includePaths = includePaths - self.makeConf = makeConf - pass - - def toText(self): - includePathShellStrings = [] - for includePath in self.includePaths: - includePathShellStrings.append('-I' + includePath.shellString() + '/') - - compiler = '' - compilerOption = '' - compilerFlags = '' - if self.sourceFilePath.getExt() in self.makeConf['c_src_exts']: - compiler = self.makeConf['cc'] - compilerOption = '-c' - compilerFlags = self.makeConf['cflags'] - elif self.sourceFilePath.getExt() in self.makeConf['cxx_src_exts']: - compiler = self.makeConf['cxxc'] - compilerOption = '-c' - compilerFlags = self.makeConf['cxxflags'] - elif self.sourceFilePath.getExt() in self.makeConf['asm_src_exts']: - compiler = self.makeConf['as'] - compilerOption = '' - compilerFlags = self.makeConf['asflags'] - - dynamic_cflags = '' - if self.makeConf['target_type'] == 'dynamic_library' and self.makeConf['fpic']: - dynamic_cflags = ' -fPIC' - - compileCommand = ('%(compiler)s %(cflags)s %(compiler_option)s -o %(objectfile)s %(include_main_path)s %(srcfile)s' % - { 'include_main_path': ' '.join(includePathShellStrings), - 'compiler': compiler, - 'cflags': compilerFlags + dynamic_cflags, - 'compiler_option': compilerOption, - 'objectfile': self.objectFilePath.shellString(), - 'srcfile': self.sourceFilePath.shellString() }) - - return compileCommand - -class GCCLinkRule(SimpleRule): - def __init__(self, targetFilePath, objectFilePaths, makeConf): - SimpleRule.__init__(self, targetFilePath, objectFilePaths) - linkAction = GCCLinkAction(targetFilePath, objectFilePaths, makeConf) - self.addBuildAction(linkAction) - -class GCCLinkAction(Action): - def __init__(self, targetFilePath, objectFilePaths, makeConf): - Action.__init__(self) - self.targetFilePath = targetFilePath - self.objectFilePaths = objectFilePaths - self.makeConf = makeConf - pass - - def toText(self): - shellStrings = [] - for objectFilePath in self.objectFilePaths: - shellStrings.append(objectFilePath.shellString()) - - libraries = self.makeConf['libraries'] - libraryFlags = [] - for library in libraries: - libraryFlags.append('-l' + library) - - libraryPaths = self.makeConf['ld_library_paths'] - libraryPathFlags = [] - for libraryPath in libraryPaths: - libraryPathFlags.append('-L' + libraryPath) - - dynamic_ldflags = '' - if self.makeConf['target_type'] == 'dynamic_library': - dynamic_ldflags = ' -shared' - - linkCommand = ('%(linker)s %(ldflags)s -o %(target_file)s %(object_files)s %(library_path_flags)s %(library_flags)s' % { - 'final_file': self.targetFilePath.shellString(), - 'object_files': ' '.join(shellStrings), - 'linker': self.makeConf['ld'], - 'ldflags': self.makeConf['ldflags'] + dynamic_ldflags, - 'target_file': self.targetFilePath.shellString(), - 'library_path_flags': ' '.join(libraryPathFlags), - 'library_flags': ' '.join(libraryFlags) - }) - - return linkCommand - -class ArRule(SimpleRule): - def __init__(self, targetFilePath, objectFilePaths, makeConf): - SimpleRule.__init__(self, targetFilePath, objectFilePaths) - arAction = ArAction(targetFilePath, objectFilePaths, makeConf) - self.addBuildAction(arAction) - -class ArAction(Action): - def __init__(self, targetFilePath, objectFilePaths, makeConf): - Action.__init__(self) - self.targetFilePath = targetFilePath - self.objectFilePaths = objectFilePaths - self.makeConf = makeConf - pass - - def toText(self): - shellStrings = [] - for objectFilePath in self.objectFilePaths: - shellStrings.append(objectFilePath.shellString()) - - arCommand = ('%(ar)s cr %(target_file)s %(object_files)s' % { - 'ar': self.makeConf['ar'], - 'target_file': self.targetFilePath.shellString(), - 'object_files': ' '.join(shellStrings) - }) - - return arCommand - -class MakeDirectoryAction(Action): - def __init__(self, directoryPath): - Action.__init__(self) - self.directoryPath = directoryPath - pass - - def toText(self): - mkDirCommand = 'mkdir -p %(directory_path)s' % { - 'directory_path': self.directoryPath.shellString() - } - - return mkDirCommand - diff --git a/deps/kake/src/main.py b/deps/kake/src/main.py deleted file mode 100755 index 30f89da..0000000 --- a/deps/kake/src/main.py +++ /dev/null @@ -1,101 +0,0 @@ -import os -import getopt -import sys -import yaml - -from modules.module import isStandardPhase, isPhaseWithConfig, isPhaseWithoutConfig, ModuleManager -from util import Configuration, ConsoleLogger - -DEFAULT_FILENAME = 'Kakefile' -WORK_PATH = '.' -logger = ConsoleLogger('main') - -def usage(): - print( - """Kake: An advanced make tool for KLUM - -h --help Show help - -v --verbose Verbose mode - -f --filename= Specify another project filename or will use default project filename - """) - sys.exit() - -def reportWrongArguments(): - logger.fatal('Error occurs when process arguments') - sys.exit() - -def reportNoProjectFile(): - logger.fatal('Project file not exists.') - sys.exit() - -def reportWrongPhase(): - logger.fatal('Phase is not standard phase.') - sys.exit() - -def existProjectFile(projectFilename): - for filename in os.listdir(WORK_PATH): - if os.path.isfile(filename) and projectFilename == filename: - return True - - return False - -def processArguments(globalConfig): - try: - options,args = getopt.getopt(sys.argv[1:], - "hvf:", ["help", "verbose", "filename="]) - except getopt.GetoptError: - reportWrongArguments() - - remainingArguments = args - for name, value in options: - if name in ("-h", "--help"): - usage() - if name in ("-v", "--verbose"): - globalConfig.setItem('mode.verbose', True) - if name in ("-f", "--filename"): - globalConfig.setItem('project.filename', value) - - return remainingArguments - -def kake(globalConfig, remainingArguments): - makePhase = globalConfig.getItem('make.phase') - if not isStandardPhase(makePhase): - reportWrongPhase() - - moduleManager = ModuleManager() - - if isPhaseWithConfig(makePhase): - projectFilename = globalConfig.getItem('project.filename') - if existProjectFile(projectFilename) == False: - reportNoProjectFile() - - projectFile = open(projectFilename) - projectFileContent = projectFile.read() - projectFile.close() - projectConfig = yaml.load(projectFileContent) - projectConfig['global'] = globalConfig.toDictionary() - projectConfig['global']['args'] = remainingArguments - - module = moduleManager.getModule(projectConfig['project']['type']) - moduleAction = moduleManager.getModuleAction(module, makePhase) - moduleAction(Configuration.fromDictionary(projectConfig)) - elif isPhaseWithoutConfig(makePhase): - module = moduleManager.getModule(remainingArguments[0]) - moduleAction = moduleManager.getModuleAction(module, makePhase) - moduleAction() - -def main(): - globalConfig = Configuration() - globalConfig.setItem('mode.verbose', False) - globalConfig.setItem('project.filename', DEFAULT_FILENAME) - globalConfig.setItem('make.phase', 'build') - - remainingArguments = processArguments(globalConfig) - - if len(remainingArguments) > 0: - globalConfig.setItem('make.phase', remainingArguments[0]) - remainingArguments = remainingArguments[1:] - - kake(globalConfig, remainingArguments) - -if __name__ == "__main__": - main() diff --git a/deps/kake/src/modules/__init__.py b/deps/kake/src/modules/__init__.py deleted file mode 100755 index e69de29..0000000 diff --git a/deps/kake/src/modules/asm/__init__.py b/deps/kake/src/modules/asm/__init__.py deleted file mode 100755 index e69de29..0000000 diff --git a/deps/kake/src/modules/cpp/__init__.py b/deps/kake/src/modules/cpp/__init__.py deleted file mode 100755 index e69de29..0000000 diff --git a/deps/kake/src/modules/cpp/module.py b/deps/kake/src/modules/cpp/module.py deleted file mode 100755 index a4ede63..0000000 --- a/deps/kake/src/modules/cpp/module.py +++ /dev/null @@ -1,242 +0,0 @@ -import os -import sys -import modules.module - -from util import Path, PathUtil, FileUtil, Configuration, ConsoleLogger -from backends.gmake.engine import Document as GMakeDocument -from backends.gmake.engine import SimpleRule as GMakeSimpleRule -from backends.gmake.engine import GCCCompileRule -from backends.gmake.engine import GCCLinkRule -from backends.gmake.engine import ArRule - -SRC_PATH = Path('src', True) -INCLUDE_PATH = Path('include', True) -TARGET_PATH = Path('target', True) -SRC_MAIN_PATH = SRC_PATH.join('main') -SRC_TEST_PATH = SRC_PATH.join('test') -INCLUDE_MAIN_PATH = INCLUDE_PATH.join('main') -INCLUDE_TEST_PATH = INCLUDE_PATH.join('test') -TARGET_OBJECT_PATH = TARGET_PATH.join('object') -TARGET_OBJECT_MAIN_PATH = TARGET_OBJECT_PATH.join('main') -TARGET_SUBMAKE_PATH = TARGET_PATH.join('submake') -TARGET_SUBMAKE_MAIN_PATH = TARGET_SUBMAKE_PATH.join('main') -TARGET_MAKEFILE_PATH = TARGET_PATH.join('Makefile') -FINAL_TARGET_SUBMAKE_PATH = TARGET_SUBMAKE_PATH.join('final_target.mk') - -class Module(modules.module.Module): - mainSourceFiles = [] - objectFiles = [] - defaultConf = {} - - def __init__(self): - modules.module.Module.__init__(self) - self.defaultConf['target_type'] = 'executable' - self.defaultConf['cc'] = 'gcc' - self.defaultConf['cxxc'] = 'g++' - self.defaultConf['as'] = 'nasm' - self.defaultConf['cflags'] = '' - self.defaultConf['cxxflags'] = '' - self.defaultConf['asflags'] = '' - self.defaultConf['fpic'] = True - self.defaultConf['autolink'] = True - self.defaultConf['ar'] = 'ar' - self.defaultConf['ld'] = 'g++' - self.defaultConf['ldflags'] = '' - self.defaultConf['ld_library_paths'] = [] - self.defaultConf['c_src_exts'] = ['.c'] - self.defaultConf['cxx_src_exts'] = ['.cpp'] - self.defaultConf['asm_src_exts'] = ['.asm'] - self.defaultConf['c_include_paths'] = [] - self.defaultConf['cxx_include_paths'] = [] - self.defaultConf['cxx_using_c_include_paths'] = True - self.defaultConf['asm_include_paths'] = [] - self.defaultConf['asm_with_object_ext'] = True - self.logger = ConsoleLogger('cmodule') - - def initKakefile(self): - kakefile = FileUtil.openFile('Kakefile') - kakefileContent = """project: - name: ${name} - version: ${version} - type: cpp""" - kakefile.write(kakefileContent) - kakefile.close() - - return True - - def init(self): - FileUtil.createDirectory(SRC_PATH) - FileUtil.createDirectory(INCLUDE_PATH) - FileUtil.createDirectory(TARGET_PATH) - FileUtil.createDirectory(SRC_MAIN_PATH) - FileUtil.createDirectory(SRC_TEST_PATH) - FileUtil.createDirectory(INCLUDE_MAIN_PATH) - FileUtil.createDirectory(INCLUDE_TEST_PATH) - - return True - - def make(self, projectConfig): - makeConf = { - 'target_type': projectConfig.getItem('make.configuration.target.type', self.defaultConf['target_type']), - 'cc': projectConfig.getItem('make.configuration.compiler.c.cc', self.defaultConf['cc']), - 'cxxc': projectConfig.getItem('make.configuration.compiler.cpp.cc', self.defaultConf['cxxc']), - 'as': projectConfig.getItem('make.configuration.compiler.asm.as', self.defaultConf['as']), - 'cflags': projectConfig.getItem('make.configuration.compiler.c.flags', self.defaultConf['cflags']), - 'cxxflags': projectConfig.getItem('make.configuration.compiler.cpp.flags', self.defaultConf['cxxflags']), - 'asflags': projectConfig.getItem('make.configuration.compiler.asm.flags', self.defaultConf['asflags']), - 'fpic': projectConfig.getItem('make.configuration.compiler.fpic', self.defaultConf['fpic']), - 'autolink': projectConfig.getItem('make.configuration.linker.autolink', self.defaultConf['autolink']), - 'ar': projectConfig.getItem('make.configuration.archiver.ar', self.defaultConf['ar']), - 'ld': projectConfig.getItem('make.configuration.linker.ld', self.defaultConf['ld']), - 'ldflags': projectConfig.getItem('make.configuration.linker.flags', self.defaultConf['ldflags']), - 'ld_library_paths': projectConfig.getItem('make.configuration.linker.library_paths', self.defaultConf['ld_library_paths']), - 'libraries': projectConfig.getItem('make.configuration.linker.libraries', []), - 'c_src_exts': projectConfig.getItem('make.configuration.compiler.c.src_exts', self.defaultConf['c_src_exts']), - 'cxx_src_exts': projectConfig.getItem('make.configuration.compiler.cpp.src_exts', self.defaultConf['cxx_src_exts']), - 'asm_src_exts': projectConfig.getItem('make.configuration.compiler.asm.src_exts', self.defaultConf['asm_src_exts']), - 'c_include_paths': projectConfig.getItem('make.configuration.compiler.c.include_paths', self.defaultConf['c_include_paths']), - 'cxx_include_paths': projectConfig.getItem('make.configuration.compiler.cpp.include_paths', self.defaultConf['cxx_include_paths']), - 'cxx_using_c_include_paths': projectConfig.getItem('make.configuration.compiler.cpp.inherit_c_include_path', self.defaultConf['cxx_using_c_include_paths']), - 'asm_include_paths': projectConfig.getItem('make.configuration.compiler.asm.include_paths', self.defaultConf['asm_include_paths']), - 'asm_with_object_ext': projectConfig.getItem('make.configuration.compiler.asm.with_object_ext', self.defaultConf['asm_with_object_ext']), - } - - makefile = GMakeDocument(TARGET_MAKEFILE_PATH) - - self.mainSourceFiles.clear() - self.objectFiles.clear() - cSourceFiles = [] - cppSourceFiles = [] - asmSourceFiles = [] - - FileUtil.searchAllFiles(cSourceFiles, SRC_MAIN_PATH, makeConf['c_src_exts']) - FileUtil.searchAllFiles(cppSourceFiles, SRC_MAIN_PATH, makeConf['cxx_src_exts']) - FileUtil.searchAllFiles(asmSourceFiles, SRC_MAIN_PATH, makeConf['asm_src_exts']) - - self.mainSourceFiles.extend(cSourceFiles) - self.mainSourceFiles.extend(cppSourceFiles) - self.mainSourceFiles.extend(asmSourceFiles) - - for fileName in self.mainSourceFiles: - filePath = Path(fileName, True) - relPath = filePath.getRelevantPath(SRC_MAIN_PATH) - dirName = relPath.getDirName() - baseName = relPath.getBaseName() - - subMakeDirPath = TARGET_SUBMAKE_MAIN_PATH.join(dirName) - FileUtil.createDirectory(subMakeDirPath) - - subMakefilePath = subMakeDirPath.join(PathUtil.getPrefix(baseName)) - subMakefilePath.appendExt('mk') - subMakefile = GMakeDocument(subMakefilePath) - - compileRule = GCCCompileRule(filePath, [INCLUDE_MAIN_PATH], SRC_MAIN_PATH, TARGET_OBJECT_MAIN_PATH, makeConf) - subMakefile.addRule(compileRule) - makefile.addSubDocument(subMakefile) - - objectFilePath = Path(compileRule.getTarget()) - self.objectFiles.append(objectFilePath) - - if makeConf['autolink']: - if makeConf['target_type'] == 'executable': - subMakefile = GMakeDocument(FINAL_TARGET_SUBMAKE_PATH) - - finalFileName = '%(name)s-%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0')} - finalFilePath = TARGET_PATH.join(finalFileName) - - linkRule = GCCLinkRule(finalFilePath, self.objectFiles, makeConf) - subMakefile.addRule(linkRule) - - makefile.addSubDocument(subMakefile) - - allRule = GMakeSimpleRule('all', [finalFilePath]) - makefile.addRule(allRule) - elif makeConf['target_type'] == 'dynamic_library': - subMakefile = GMakeDocument(FINAL_TARGET_SUBMAKE_PATH) - - finalFileName = 'lib%(name)s.so.%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0')} - finalFilePath = TARGET_PATH.join(finalFileName) - - linkRule = GCCLinkRule(finalFilePath, self.objectFiles, makeConf) - subMakefile.addRule(linkRule) - - makefile.addSubDocument(subMakefile) - - allRule = GMakeSimpleRule('all', [finalFilePath]) - makefile.addRule(allRule) - elif makeConf['target_type'] == 'static_library': - subMakefile = GMakeDocument(FINAL_TARGET_SUBMAKE_PATH) - - finalFileName = 'lib%(name)s.a.%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0')} - finalFilePath = TARGET_PATH.join(finalFileName) - - arRule = ArRule(finalFilePath, self.objectFiles, makeConf) - subMakefile.addRule(arRule) - - makefile.addSubDocument(subMakefile) - - allRule = GMakeSimpleRule('all', [finalFilePath]) - makefile.addRule(allRule) - else: - self.logger.warn('target_type is not recognized!') - sys.exit(1) - else: - allRule = GMakeSimpleRule('all', self.objectFiles) - makefile.addRule(allRule) - - makefile.writeToFile() - - return True - - def build(self, projectConfig): - returnValue = os.system('make -f %(makefile)s' % { - 'makefile': TARGET_PATH.join('Makefile').shellString() }) - - if returnValue == 0: - return True - else: - return False - - def test(self): - pass - - def package(self): - pass - - def deploy(self): - pass - - def run(self, projectConfig): - finalFileName = '%(name)s-%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0') } - finalFilePath = TARGET_PATH.join(finalFileName) - arguments = projectConfig.getItem("global.args", []) - shellArguments = [] - for argument in arguments: - shellArgument = '"' + argument + '"' - shellArguments.append(shellArgument) - - runCommand = 'cd target && %(file_path)s %(arguments)s' % { - 'file_path': finalFilePath.shellString(), - 'arguments': ' '.join(shellArguments) - } - - return os.system(runCommand) - - def clean(self, projectConfig): - for cleanPath in (TARGET_OBJECT_PATH, - TARGET_OBJECT_MAIN_PATH, - TARGET_SUBMAKE_PATH, - TARGET_SUBMAKE_MAIN_PATH, - TARGET_MAKEFILE_PATH): - self.logger.debug('Remove: ' + str(cleanPath)) - os.system('rm -rf ' + cleanPath.shellString()) - - return True diff --git a/deps/kake/src/modules/gmake/__init__.py b/deps/kake/src/modules/gmake/__init__.py deleted file mode 100755 index e69de29..0000000 diff --git a/deps/kake/src/modules/gmake/module.py b/deps/kake/src/modules/gmake/module.py deleted file mode 100755 index 0fe8f12..0000000 --- a/deps/kake/src/modules/gmake/module.py +++ /dev/null @@ -1,226 +0,0 @@ -import os -import sys -import modules.module - -from util import Path, PathUtil, FileUtil, Configuration, ConsoleLogger -from backends.gmake.engine import Document as GMakeDocument -from backends.gmake.engine import SimpleRule as GMakeSimpleRule -from backends.gmake.engine import GCCCompileRule -from backends.gmake.engine import GCCLinkRule -from backends.gmake.engine import ArRule - -SRC_PATH = Path('src', True) -INCLUDE_PATH = Path('include', True) -TARGET_PATH = Path('target', True) -SRC_MAIN_PATH = SRC_PATH.join('main') -SRC_TEST_PATH = SRC_PATH.join('test') -INCLUDE_MAIN_PATH = INCLUDE_PATH.join('main') -INCLUDE_TEST_PATH = INCLUDE_PATH.join('test') -TARGET_OBJECT_PATH = TARGET_PATH.join('object') -TARGET_OBJECT_MAIN_PATH = TARGET_OBJECT_PATH.join('main') -TARGET_SUBMAKE_PATH = TARGET_PATH.join('submake') -TARGET_SUBMAKE_MAIN_PATH = TARGET_SUBMAKE_PATH.join('main') -TARGET_MAKEFILE_PATH = TARGET_PATH.join('Makefile') -FINAL_TARGET_SUBMAKE_PATH = TARGET_SUBMAKE_PATH.join('final_target.mk') - -class Module(modules.module.Module): - mainSourceFiles = [] - objectFiles = [] - defaultConf = {} - - def __init__(self): - self.defaultConf['target_type'] = 'executable' - self.defaultConf['cc'] = 'gcc' - self.defaultConf['cxxc'] = 'g++' - self.defaultConf['cflags'] = '' - self.defaultConf['cxxflags'] = '' - self.defaultConf['fpic'] = True - self.defaultConf['autolink'] = True - self.defaultConf['ar'] = 'ar' - self.defaultConf['ld'] = 'g++' - self.defaultConf['ldflags'] = '' - self.defaultConf['ld_library_paths'] = [] - self.defaultConf['c_src_exts'] = ['.c'] - self.defaultConf['cxx_src_exts'] = ['.cpp'] - self.defaultConf['c_include_paths'] = [] - self.defaultConf['cxx_include_paths'] = [] - self.defaultConf['cxx_using_c_include_paths'] = True - self.logger = ConsoleLogger('cmodule') - - def initKakefile(self): - kakefile = FileUtil.openFile('Kakefile') - kakefileContent = """project: - name: ${name} - version: ${version} - type: cpp""" - kakefile.write(kakefileContent) - kakefile.close() - - return True - - def init(self): - FileUtil.createDirectory(SRC_PATH) - FileUtil.createDirectory(INCLUDE_PATH) - FileUtil.createDirectory(TARGET_PATH) - FileUtil.createDirectory(SRC_MAIN_PATH) - FileUtil.createDirectory(SRC_TEST_PATH) - FileUtil.createDirectory(INCLUDE_MAIN_PATH) - FileUtil.createDirectory(INCLUDE_TEST_PATH) - - return True - - def make(self, projectConfig): - makeConf = { - 'target_type': projectConfig.getItem('make.configuration.target.type', self.defaultConf['target_type']), - 'cc': projectConfig.getItem('make.configuration.compiler.c.cc', self.defaultConf['cc']), - 'cxxc': projectConfig.getItem('make.configuration.compiler.cpp.cc', self.defaultConf['cxxc']), - 'cflags': projectConfig.getItem('make.configuration.compiler.c.flags', self.defaultConf['cflags']), - 'cxxflags': projectConfig.getItem('make.configuration.compiler.cpp.flags', self.defaultConf['cxxflags']), - 'fpic': projectConfig.getItem('make.configuration.compiler.fpic', self.defaultConf['fpic']), - 'autolink': projectConfig.getItem('make.configuration.linker.autolink', self.defaultConf['autolink']), - 'ar': projectConfig.getItem('make.configuration.archiver.ar', self.defaultConf['ar']), - 'ld': projectConfig.getItem('make.configuration.linker.ld', self.defaultConf['ld']), - 'ldflags': projectConfig.getItem('make.configuration.linker.flags', self.defaultConf['ldflags']), - 'ld_library_paths': projectConfig.getItem('make.configuration.linker.library_paths', self.defaultConf['ld_library_paths']), - 'libraries': projectConfig.getItem('make.configuration.linker.libraries', []), - 'c_src_exts': projectConfig.getItem('make.configuration.compiler.c.src_exts', self.defaultConf['c_src_exts']), - 'cxx_src_exts': projectConfig.getItem('make.configuration.compiler.cpp.src_exts', self.defaultConf['cxx_src_exts']), - 'c_include_paths': projectConfig.getItem('make.configuration.compiler.c.include_paths', self.defaultConf['c_include_paths']), - 'cxx_include_paths': projectConfig.getItem('make.configuration.compiler.cpp.include_paths', self.defaultConf['cxx_include_paths']), - 'cxx_using_c_include_paths': projectConfig.getItem('make.configuration.compiler.cpp.inherit_c_include_path', self.defaultConf['cxx_using_c_include_paths']) - } - - makefile = GMakeDocument(TARGET_MAKEFILE_PATH) - - self.mainSourceFiles.clear() - self.objectFiles.clear() - cSourceFiles = [] - cppSourceFiles = [] - FileUtil.searchAllFiles(cSourceFiles, SRC_MAIN_PATH, makeConf['c_src_exts']) - FileUtil.searchAllFiles(cppSourceFiles, SRC_MAIN_PATH, makeConf['cxx_src_exts']) - self.mainSourceFiles.extend(cSourceFiles) - self.mainSourceFiles.extend(cppSourceFiles) - - for fileName in self.mainSourceFiles: - filePath = Path(fileName, True) - relPath = filePath.getRelevantPath(SRC_MAIN_PATH) - dirName = relPath.getDirName() - baseName = relPath.getBaseName() - - subMakeDirPath = TARGET_SUBMAKE_MAIN_PATH.join(dirName) - FileUtil.createDirectory(subMakeDirPath) - - subMakefilePath = subMakeDirPath.join(PathUtil.getPrefix(baseName)) - subMakefilePath.appendExt('mk') - subMakefile = GMakeDocument(subMakefilePath) - - compileRule = GCCCompileRule(filePath, [INCLUDE_MAIN_PATH], SRC_MAIN_PATH, TARGET_OBJECT_MAIN_PATH, makeConf) - subMakefile.addRule(compileRule) - makefile.addSubDocument(subMakefile) - - objectFilePath = Path(compileRule.getTarget()) - self.objectFiles.append(objectFilePath) - - if makeConf['autolink']: - if makeConf['target_type'] == 'executable': - subMakefile = GMakeDocument(FINAL_TARGET_SUBMAKE_PATH) - - finalFileName = '%(name)s-%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0')} - finalFilePath = TARGET_PATH.join(finalFileName) - - linkRule = GCCLinkRule(finalFilePath, self.objectFiles, makeConf) - subMakefile.addRule(linkRule) - - makefile.addSubDocument(subMakefile) - - allRule = GMakeSimpleRule('all', [finalFilePath]) - makefile.addRule(allRule) - elif makeConf['target_type'] == 'dynamic_library': - subMakefile = GMakeDocument(FINAL_TARGET_SUBMAKE_PATH) - - finalFileName = 'lib%(name)s.so.%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0')} - finalFilePath = TARGET_PATH.join(finalFileName) - - linkRule = GCCLinkRule(finalFilePath, self.objectFiles, makeConf) - subMakefile.addRule(linkRule) - - makefile.addSubDocument(subMakefile) - - allRule = GMakeSimpleRule('all', [finalFilePath]) - makefile.addRule(allRule) - elif makeConf['target_type'] == 'static_library': - subMakefile = GMakeDocument(FINAL_TARGET_SUBMAKE_PATH) - - finalFileName = 'lib%(name)s.a.%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0')} - finalFilePath = TARGET_PATH.join(finalFileName) - - arRule = ArRule(finalFilePath, self.objectFiles, makeConf) - subMakefile.addRule(arRule) - - makefile.addSubDocument(subMakefile) - - allRule = GMakeSimpleRule('all', [finalFilePath]) - makefile.addRule(allRule) - else: - self.logger.warn('target_type is not recognized!') - sys.exit(1) - else: - allRule = GMakeSimpleRule('all', self.objectFiles) - makefile.addRule(allRule) - - makefile.writeToFile() - - return True - - def build(self, projectConfig): - returnValue = os.system('make -f %(makefile)s' % { - 'makefile': TARGET_PATH.join('Makefile').shellString() }) - - if returnValue == 0: - return True - else: - return False - - def test(self): - pass - - def package(self): - pass - - def deploy(self): - pass - - def run(self, projectConfig): - finalFileName = '%(name)s-%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0') } - finalFilePath = TARGET_PATH.join(finalFileName) - arguments = projectConfig.getItem("global.args", []) - shellArguments = [] - for argument in arguments: - shellArgument = '"' + argument + '"' - shellArguments.append(shellArgument) - - runCommand = 'cd target && %(file_path)s %(arguments)s' % { - 'file_path': finalFilePath.shellString(), - 'arguments': ' '.join(shellArguments) - } - - return os.system(runCommand) - - def clean(self, projectConfig): - for cleanPath in (TARGET_OBJECT_PATH, - TARGET_OBJECT_MAIN_PATH, - TARGET_SUBMAKE_PATH, - TARGET_SUBMAKE_MAIN_PATH, - TARGET_MAKEFILE_PATH): - self.logger.debug('Remove: ' + str(cleanPath)) - os.system('rm -rf ' + cleanPath.shellString()) - - return True diff --git a/deps/kake/src/modules/ld/__init__.py b/deps/kake/src/modules/ld/__init__.py deleted file mode 100755 index e69de29..0000000 diff --git a/deps/kake/src/modules/ld/module.py b/deps/kake/src/modules/ld/module.py deleted file mode 100755 index 987b33a..0000000 --- a/deps/kake/src/modules/ld/module.py +++ /dev/null @@ -1,180 +0,0 @@ -import os -import modules.module - -from util import Path, PathUtil, FileUtil, Configuration, ConsoleLogger -from backends.gmake.engine import Document as GMakeDocument -from backends.gmake.engine import SimpleRule as GMakeSimpleRule -from backends.gmake.engine import GCCCompileRule -from backends.gmake.engine import GCCLinkRule -from backends.gmake.engine import ArRule - -TARGET_PATH = Path('target', True) -TARGET_MAKEFILE_PATH = TARGET_PATH.join('Makefile') -TARGET_SUBMAKE_PATH = TARGET_PATH.join('submake') -FINAL_TARGET_SUBMAKE_PATH = TARGET_SUBMAKE_PATH.join('final_target') - -class Module(modules.module.Module): - objectFiles = [] - defaultConf = {} - - def __init__(self): - modules.module.Module.__init__(self) - self.defaultConf['target_type'] = 'executable' - self.defaultConf['ar'] = 'ar' - self.defaultConf['ld'] = 'g++' - self.defaultConf['ldflags'] = '' - self.defaultConf['ld_library_paths'] = [] - self.logger = ConsoleLogger('ldmodule') - - def initKakefile(self): - kakefile = FileUtil.openFile('Kakefile') - kakefileContent = """project: - name: ${name} - version: ${version} - type: ld - - make: - configuration: - linker: - include: [ ]""" - kakefile.write(kakefileContent) - kakefile.close() - - return True - - def init(self): - FileUtil.createDirectory(TARGET_PATH) - - return True - - def make(self, projectConfig): - makeConf = { - 'target_type': projectConfig.getItem('make.configuration.target.type', self.defaultConf['target_type']), - 'ar': projectConfig.getItem('make.configuration.archiver.ar', self.defaultConf['ar']), - 'ld': projectConfig.getItem('make.configuration.linker.ld', self.defaultConf['ld']), - 'ldflags': projectConfig.getItem('make.configuration.linker.flags', self.defaultConf['ldflags']), - 'ld_library_paths': projectConfig.getItem('make.configuration.linker.library_paths', self.defaultConf['ld_library_paths']), - 'libraries': projectConfig.getItem('make.configuration.linker.libraries', []), - 'solution_dir': projectConfig.getItem('global.solution.dir', '..'), - 'sources': projectConfig.getItem('make.configuration.linker.include', []), - } - - makefile = GMakeDocument(TARGET_MAKEFILE_PATH) - objectSourcePaths = [] - - self.objectFiles.clear() - solutionDirPath = Path(makeConf['solution_dir'], True) - objectFilePaths = [] - for objectSourcePath in makeConf['sources']: - objectSourcePath = solutionDirPath.join(objectSourcePath) - FileUtil.searchAllFiles(objectFilePaths, objectSourcePath, [ '.o' ]) - - for objectFilePath in objectFilePaths: - self.objectFiles.append(Path(objectFilePath)) - - finalFilePath = self.getFinalFilePath(projectConfig, makeConf) - linkRule = self.getLinkRule(finalFilePath, makeConf) - subMakefile = GMakeDocument(FINAL_TARGET_SUBMAKE_PATH) - subMakefile.addRule(linkRule) - - makefile.addSubDocument(subMakefile) - - allRule = GMakeSimpleRule('all', [finalFilePath]) - makefile.addRule(allRule) - - makefile.writeToFile() - - return True - - def build(self, projectConfig): - returnValue = os.system('make -f %(makefile)s' % { - 'makefile': TARGET_PATH.join('Makefile').shellString() }) - - if returnValue == 0: - return True - else: - return False - - def test(self): - pass - - def package(self): - pass - - def deploy(self): - pass - - def run(self, projectConfig): - finalFileName = '%(name)s-%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0') } - finalFilePath = TARGET_PATH.join(finalFileName) - arguments = projectConfig.getItem("global.args", []) - shellArguments = [] - for argument in arguments: - shellArgument = '"' + argument + '"' - shellArguments.append(shellArgument) - - runCommand = 'cd target && %(file_path)s %(arguments)s' % { - 'file_path': finalFilePath.shellString(), - 'arguments': ' '.join(shellArguments) - } - - return os.system(runCommand) - - def clean(self, projectConfig): - makeConf = self.getMakeConf(projectConfig) - finalFilePath = self.getFinalFilePath(projectConfig, makeConf) - - for cleanPath in (TARGET_SUBMAKE_PATH, - TARGET_MAKEFILE_PATH, - FINAL_TARGET_SUBMAKE_PATH, - finalFilePath): - self.logger.debug('Remove: ' + str(cleanPath)) - os.system('rm -rf ' + cleanPath.shellString()) - - return True - - def getMakeConf(self, projectConfig): - return { - 'target_type': projectConfig.getItem('make.configuration.target.type', self.defaultConf['target_type']), - 'ar': projectConfig.getItem('make.configuration.archiver.ar', self.defaultConf['ar']), - 'ld': projectConfig.getItem('make.configuration.linker.ld', self.defaultConf['ld']), - 'ldflags': projectConfig.getItem('make.configuration.linker.flags', self.defaultConf['ldflags']), - 'ld_library_paths': projectConfig.getItem('make.configuration.linker.library_paths', self.defaultConf['ld_library_paths']), - 'libraries': projectConfig.getItem('make.configuration.linker.libraries', []), - 'solution_dir': projectConfig.getItem('global.solution.dir', '..'), - 'sources': projectConfig.getItem('make.configuration.linker.include', []), - } - - - def getFinalFilePath(self, projectConfig, makeConf): - finalFileName = '' - if makeConf['target_type'] == 'executable': - finalFileName = '%(name)s-%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0')} - elif makeConf['target_type'] == 'dynamic_library': - finalFileName = 'lib%(name)s.so.%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0')} - elif makeConf['target_type'] == 'static_library': - finalFileName = 'lib%(name)s.a.%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0')} - - return TARGET_PATH.join(finalFileName) - - def getLinkRule(self, finalFilePath, makeConf): - linkRule = None - if makeConf['target_type'] == 'executable': - linkRule = GCCLinkRule(finalFilePath, self.objectFiles, makeConf) - elif makeConf['target_type'] == 'dynamic_library': - linkRule = GCCLinkRule(finalFilePath, self.objectFiles, makeConf) - elif makeConf['target_type'] == 'static_library': - linkRule = ArRule(finalFilePath, self.objectFiles, makeConf) - else: - self.logger.warn('target_type is not recognized!') - sys.exit(1) - - return linkRule diff --git a/deps/kake/src/modules/module.py b/deps/kake/src/modules/module.py deleted file mode 100755 index 9e9c076..0000000 --- a/deps/kake/src/modules/module.py +++ /dev/null @@ -1,156 +0,0 @@ -import os -import util -from util import Path -from util import PathUtil, FileUtil -from util import ConsoleLogger - -FUNCTIONS_WITH_CONF = set(['make', 'build', 'run', 'clean']) -FUNCTIONS_WITHOUT_CONF = set(['init']) -logger = ConsoleLogger('module') - -STANDARD_PHASE = set(['init', 'make', 'build', 'test', 'package', 'deploy', 'run', 'clean']) - -def MakeBuildVersion(): - versionFile = None - if not PathUtil.exist('target/BUILD_VERSION'): - versionFile = FileUtil.openFile('target/BUILD_VERSION') - else: - versionFile = open('target/BUILD_VERSION', 'w') - versionFile.close() - -def KakefileChanged(): - if PathUtil.exist('target/BUILD_VERSION'): - kakefileModifiedTime = os.path.getmtime('Kakefile') - lastBuildTime = os.path.getmtime('target/BUILD_VERSION') - if kakefileModifiedTime > lastBuildTime: - return True - - return False - -class Module(object): - def initCaller(self): - self.enterPhase('Init') - if not PathUtil.exist('Kakefile'): - self.initKakefile() - else: - logger.debug('Skip initialize Kakefile') - self.init() - self.exitPhase('Init') - - def makeCaller(self, projectConfig): - self.enterPhase('Make') - isSuccessful = self.make(projectConfig) - self.exitPhase('Make') - - return isSuccessful - - def buildCaller(self, projectConfig): - if KakefileChanged(): - self.cleanCaller(projectConfig) - - if not self.makeCaller(projectConfig): - return False - - self.enterPhase('Build') - isSuccessful = self.build(projectConfig) - MakeBuildVersion() - self.exitPhase('Build') - - return isSuccessful - - def testCaller(self): - self.enterPhase('Test') - self.test() - self.exitPhase('Test') - - def packageCaller(self): - self.enterPhase('Package') - self.package() - self.exitPhase('Package') - - def deployCaller(self): - self.enterPhase('Deployment') - self.deploy() - self.exitPhase('Deployment') - - def runCaller(self, projectConfig): - if not self.buildCaller(projectConfig): - return False - - self.enterPhase('Run') - returnValue = self.run(projectConfig) - self.exitPhase('Run') - - return returnValue - - def cleanCaller(self, projectConfig): - self.enterPhase('Clean') - self.clean(projectConfig) - self.exitPhase('Clean') - - def enterPhase(self, phaseName): - logger.info('Enter phase: ' + phaseName) - - def exitPhase(self, phaseName): - logger.info('Exit phase: ' + phaseName) - - def initKakefile(self): - raise NotImplementedError() - def init(self): - raise NotImplementedError() - def make(self, projectConfig): - raise NotImplementedError() - def build(self, projectConfig): - raise NotImplementedError() - def test(self): - raise NotImplementedError() - def package(self): - raise NotImplementedError() - def deploy(self): - raise NotImplementedError() - def run(self, projectConfig): - raise NotImplementedError() - def clean(self, projectConfig): - raise NotImplementedError() - -def isStandardPhase(phase): - return phase in STANDARD_PHASE - -def isPhaseWithConfig(phase): - return phase in FUNCTIONS_WITH_CONF - -def isPhaseWithoutConfig(phase): - return phase in FUNCTIONS_WITHOUT_CONF - -class ModuleManager(object): - moduleList = [] - - def __init__(self): - self.scanModules() - - def scanModules(self): - modules = __import__('modules') - modulesPath = modules.__path__[0] - modulesSubFiles = os.listdir(modulesPath) - for modulesSubFile in modulesSubFiles: - if os.path.isdir(PathUtil.join(modulesPath, modulesSubFile)) and not modulesSubFile.startswith('__'): - self.moduleList.append(modulesSubFile) - - def getModule(self, projectType): - if not projectType in self.moduleList: - self.reportProjectTypeNotSupported() - - moduleName = 'modules.' + projectType + '.module' - moduleFile = __import__(moduleName, {}, {}, ['a']) - module = moduleFile.Module() - - return module - - def reportProjectTypeNotSupported(self): - logger.fatal('The project type is not supported') - sys.exit() - - def getModuleAction(self, module, phase): - if hasattr(module, phase): - return getattr(module, phase + 'Caller') - return None diff --git a/deps/kake/src/modules/solution/__init__.py b/deps/kake/src/modules/solution/__init__.py deleted file mode 100755 index e69de29..0000000 diff --git a/deps/kake/src/modules/solution/module.py b/deps/kake/src/modules/solution/module.py deleted file mode 100755 index d6dccfa..0000000 --- a/deps/kake/src/modules/solution/module.py +++ /dev/null @@ -1,108 +0,0 @@ -import os -import sys -import modules.module - -from util import Path, PathUtil, FileUtil, Configuration, ConsoleLogger -from backends.gmake.engine import Document as GMakeDocument -from backends.gmake.engine import SimpleRule as GMakeSimpleRule -from backends.gmake.engine import GCCCompileRule -from backends.gmake.engine import GCCLinkRule -from backends.gmake.engine import ArRule - -PROJECT_PATH = Path('project', True) -TARGET_PATH = Path('target', True) - -class Module(modules.module.Module): - defaultConf = {} - projects = [] - - def __init__(self): - #'solution_dir': projectConfig.getItem('global.solution.dir', '..'), - self.logger = ConsoleLogger('cmodule') - - projectNames = os.listdir('project') - projectNames.sort() - for projectName in projectNames: - project = { - 'name': projectName, - 'path': PROJECT_PATH.join(projectName) - } - self.projects.append(project) - - - def initKakefile(self): - kakefile = FileUtil.openFile('Kakefile') - kakefileContent = """project: - name: ${name} - version: ${version} - type: solution - -run: - default: ${default}""" - kakefile.write(kakefileContent) - kakefile.close() - - return True - - def init(self): - FileUtil.createDirectory(PROJECT_PATH) - FileUtil.createDirectory(TARGET_PATH) - - return True - - def make(self, projectConfig): - return True - - def build(self, projectConfig): - for project in self.projects: - projectFilename = globalConfig.getItem('project.filename') - if existProjectFile(projectFilename) == False: - reportNoProjectFile() - - projectFile = open(projectFilename) - projectFileContent = projectFile.read() - projectFile.close() - projectConfig = yaml.load(projectFileContent) - projectConfig['global'] = globalConfig.toDictionary() - projectConfig['global']['args'] = remainingArguments - - module = moduleManager.getModule(projectConfig['project']['type']) - moduleAction = moduleManager.getModuleAction(module, makePhase) - moduleAction(Configuration.fromDictionary(projectConfig)) - - - returnValue = 0 - if returnValue == 0: - return True - else: - return False - - def test(self): - pass - - def package(self): - pass - - def deploy(self): - pass - - def run(self, projectConfig): - finalFileName = '%(name)s-%(version)s' % { - 'name': projectConfig.getItem('project.name', 'noname'), - 'version': projectConfig.getItem('project.version', '1.0.0') } - finalFilePath = TARGET_PATH.join(finalFileName) - arguments = projectConfig.getItem("global.args", []) - shellArguments = [] - for argument in arguments: - shellArgument = '"' + argument + '"' - shellArguments.append(shellArgument) - - runCommand = 'cd target && %(file_path)s %(arguments)s' % { - 'file_path': finalFilePath.shellString(), - 'arguments': ' '.join(shellArguments) - } - - return os.system(runCommand) - - def clean(self, projectConfig): - return True diff --git a/deps/kake/src/runtest.py b/deps/kake/src/runtest.py deleted file mode 100755 index 86b4a68..0000000 --- a/deps/kake/src/runtest.py +++ /dev/null @@ -1,83 +0,0 @@ -#! /usr/bin/python3 - -from unittest import TestLoader, TextTestRunner, TestSuite -import os.path -import test.util - -class AutomaticTestRunner: - def __init__(self): - self.loader = TestLoader() - self.runner = TextTestRunner() - - def runTests(self): - testCaseNames = self.findTestCaseNames() - testCaseModules = self.loadTestCaseModules(testCaseNames) - testSuite = self.loadTestsFromModules(testCaseModules) - return self.runner.run(testSuite) - - def findTestCaseNames(self): - rootSuite = __import__('test') - rootSuitePath = rootSuite.__path__[0] - return self.findTestCaseNamesFromDirectory(rootSuitePath) - - def loadTestCaseModules(self, testCaseNames): - testCaseModules = [] - for testCaseName in testCaseNames: - testCaseModule = __import__(testCaseName, {}, {}, ['a']) - testCaseModules.append(testCaseModule) - - return testCaseModules - - def loadTestsFromModules(self, testCaseModules): - suite = TestSuite() - for testCaseModule in testCaseModules: - subSuite = self.loader.loadTestsFromModule(testCaseModule) - suite.addTest(subSuite) - - return suite - - def findTestCaseNamesFromDirectory(self, rootPath): - subFiles = os.listdir(rootPath) - - testModuleNames = self.findSubModuleNames(rootPath, subFiles) - subSuitePaths = self.findSubSuitePaths(rootPath, subFiles) - subSuiteTestModuleNames = self.findTestCaseNamesFromSubSuitePaths(subSuitePaths) - testModuleNames.extend(subSuiteTestModuleNames) - - return testModuleNames - - def findSubModuleNames(self, rootPath, subFiles): - testModuleNames = [] - for subFile in subFiles: - subFilePath = rootPath + '/' + subFile - if os.path.isfile(subFilePath) and subFile.endswith('.py') and not subFile.startswith('__'): - rootModuleName = os.path.relpath(rootPath).replace('/', '.') - testModuleName = rootModuleName + '.' + subFile[0:-3] - testModuleNames.append(testModuleName) - - return testModuleNames - - def findSubSuitePaths(self, rootPath, subFiles): - subSuitePaths = [] - for subFile in subFiles: - subFilePath = rootPath + '/' + subFile - if os.path.isdir(subFilePath) and not subFile.startswith('__'): - subSuitePaths.append(subFilePath) - - return subSuitePaths - - def findTestCaseNamesFromSubSuitePaths(self, subSuitePaths): - testModuleNames = [] - - for subSuitePath in subSuitePaths: - subTestModuleNames = self.findTestCaseNamesFromDirectory(subSuitePath) - testModuleNames.extend(subTestModuleNames) - - return testModuleNames - -def main(): - testRunner = AutomaticTestRunner() - testResult = testRunner.runTests() - -if __name__ == '__main__': - main() diff --git a/deps/kake/src/util.py b/deps/kake/src/util.py deleted file mode 100755 index 02eb361..0000000 --- a/deps/kake/src/util.py +++ /dev/null @@ -1,196 +0,0 @@ -import os -import sys -import logging - -class ConsoleLogger(object): - def __init__(self, name): - handler = logging.StreamHandler(sys.stdout) - fmt = '[%(levelname)s] %(message)s' - formatter = logging.Formatter(fmt) - handler.setFormatter(formatter) - - self.logger = logging.getLogger(name) - self.logger.addHandler(handler) - self.logger.setLevel(logging.DEBUG) - - def critical(self, msg, *args, **kwargs): - self.logger.critical(msg, *args, **kwargs) - - def debug(self, msg, *args, **kwargs): - self.logger.debug(msg, *args, **kwargs) - - def error(self, msg, *args, **kwargs): - self.logger.error(msg, *args, **kwargs) - - def exception(self, msg, *args, **kwargs): - self.logger.exception(msg, *args, **kwargs) - - def fatal(self, msg, *args, **kwargs): - self.logger.fatal(msg, *args, **kwargs) - - def info(self, msg, *args, **kwargs): - self.logger.info(msg, *args, **kwargs) - - def warn(self, msg, *args, **kwargs): - self.logger.warn(msg, *args, **kwargs) - -logger = ConsoleLogger('util') - -class PathUtil(object): - @classmethod - def join(cls, path1, path2): - return Path(path1).join(path2).string() - - @classmethod - def getPrefix(cls, path): - return Path(path).getPrefix() - - @classmethod - def exist(cls, filePath): - filename = str(filePath) - return os.path.exists(str(filename)) - - @classmethod - def toPathList(cls, stringList, isAbsolutePath = False): - pathList = [] - for string in stringList: - pathList.append(Path(string, isAbsolutePath)) - - return pathList - -class FileUtil(object): - @classmethod - def openFile(cls, filePath): - filePath = Path(filePath) - dirName = filePath.getDirName() - if dirName and not os.path.exists(dirName): - os.makedirs(dirName) - return open(filePath.string(), 'w') - - @classmethod - def createDirectory(cls, dirPath): - dirName = str(dirPath) - if os.path.exists(dirName): - logger.debug('Skip creating exists directory: %(dirName)s' % {'dirName': dirName}) - else: - logger.debug('Create directory: %(dirName)s' % {'dirName': dirName}) - os.makedirs(dirName) - - @classmethod - def searchAllFiles(cls, fileList, root, exts): - for dirPath, dirNames, fileNames in os.walk(str(root), True): - for fileName in fileNames: - filePath = Path(fileName) - if filePath.getExt() in exts: - fileList.append(PathUtil.join(dirPath, fileName)) - -class Path(object): - path = "" - absolute = False - - def __init__(self): - self.path = "" - self.absolute = False - pass - - def __init__(self, path, isAbsolutePath = False): - if isAbsolutePath: - self.setAbsolutePath(path) - else: - self.setRelevantPath(path) - - def __str__(self): - return self.string() - - def string(self): - return self.path - - def shellString(self): - return self.path.replace(' ', '\\ ') - - def clone(self): - return Path(self.getPath(), self.absolute) - - def setAbsolutePath(self, path): - self.path = os.path.abspath(str(path)) - self.absolute = True - - def setRelevantPath(self, path): - self.path = str(path) - self.absolute = False - - def getPath(self): - return self.path - - def getRelevantPath(self, rootPath): - return Path(os.path.relpath(self.path, str(rootPath)), False) - - def getDirName(self): - return os.path.dirname(self.path) - - def getBaseName(self): - return os.path.basename(self.path) - - def join(self, subPath): - return self.clone().joinInPlace(subPath) - - def joinInPlace(self, subPath): - if ( self.path.endswith('/') ): - self.path = self.path + str(subPath) - else: - self.path = self.path + "/" + str(subPath) - - return self - - def getPrefix(self): - return os.path.extsep.join(os.path.splitext(self.path)[:-1]) - - def getExt(self): - return os.path.splitext(self.path)[-1] - - def appendExt(self, ext): - self.path = self.path + os.path.extsep + ext - - def removeExt(self, ext): - self.path = self.getPrefix() - - def replaceExt(self, ext): - self.path = self.getPrefix() + os.path.extsep + ext - -class Configuration(object): - def __init__(self): - self.configuration = {} - - @classmethod - def fromDictionary(cls, dictionary): - configuration = Configuration() - configuration.configuration = dictionary - return configuration - - def toDictionary(self): - return self.configuration - - def getItem(self, path, defaultValue = None): - keys = path.split('.') - currentValue = self.configuration - try: - for key in keys: - currentValue = currentValue[key] - except KeyError: - currentValue = defaultValue - - return currentValue - - def setItem(self, path, value): - keys = path.split('.') - currentDictionary = self.configuration - - keyIndex = 0 - for key in keys: - keyIndex = keyIndex + 1 - if not keyIndex == len(keys): - if key not in currentDictionary: - currentDictionary[key] = {} - currentDictionary = currentDictionary[key] - else: - currentDictionary[key] = value diff --git a/deps/kake/templates/project/CppProject.kake b/deps/kake/templates/project/CppProject.kake new file mode 100644 index 0000000..c6a2b1b --- /dev/null +++ b/deps/kake/templates/project/CppProject.kake @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = { + name: 'CppProject', + version: '0.0.1', + type: 'cpp', + target: 'executable', + targetPath: '', + compiler: { + cxx: 'g++', + cxxflags: ['-std=c++11'], + src: [ + ], + includePaths: [ + ] + }, + linker: { + ld: 'g++', + } +}; diff --git a/deps/kake/templates/solution/SolutionSample.kake b/deps/kake/templates/solution/SolutionSample.kake new file mode 100644 index 0000000..4813b0c --- /dev/null +++ b/deps/kake/templates/solution/SolutionSample.kake @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = { + name: 'SampleSolution', + version: '0.0.1', + type: 'solution', + projects: [ + ] +}; diff --git a/deps/kake/templates/solution/deps.kake b/deps/kake/templates/solution/deps.kake new file mode 100644 index 0000000..8eb077a --- /dev/null +++ b/deps/kake/templates/solution/deps.kake @@ -0,0 +1,4 @@ +'use strict'; + +module.exports = { +}; diff --git a/deps/meshy/CMakeLists.txt b/deps/meshy/CMakeLists.txt deleted file mode 100755 index de597c7..0000000 --- a/deps/meshy/CMakeLists.txt +++ /dev/null @@ -1,76 +0,0 @@ -cmake_minimum_required(VERSION 3.3) -project(net_framework) - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - -set(SOURCE_FILES - include/bsd/net_bsd.h - include/epoll/EPollClient.h - include/epoll/EPollConnection.h - include/epoll/epollloop.h - include/epoll/EPollServer.h - include/epoll/EPollStream.h - include/iocp/IOCPClient.h - include/iocp/IOCPConnection.h - include/iocp/IOCPLoop.h - include/iocp/IOCPServer.h - include/iocp/IOCPStream.h - include/kqueue/kqueue.h - include/kqueue/kqueueloop.h - include/linux/common.h - include/linux/net_linux.h - include/template/utils/thread_pool.tcc - include/utils/common_utils.h - include/utils/concurrent_queue.h - include/utils/logger.h - include/utils/thread_pool.h - include/utils/time.h - include/utils/String.h - include/win32/net_win32.h - include/bytearray.h - include/DataSink.h - include/eventqueue.h - include/eventqueueloop.h - include/IoLoop.h - include/loop.h - include/net.h - include/PackageDataSink.h - include/Meshy.h - src/epoll/EPollClient.cpp - src/epoll/EPollConnection.cpp - src/epoll/epollloop.cpp - src/epoll/EPollServer.cpp - src/epoll/EPollStream.cpp - src/iocp/IOCPClient.cpp - src/iocp/IOCPConnection.cpp - src/iocp/iocploop.cpp - src/iocp/IOCPServer.cpp - src/iocp/IOCPStream.cpp - src/kqueue/kqueue.cpp - src/kqueue/kqueueloop.cpp - src/utils/common_utils.cpp - src/utils/logger.cpp - src/utils/thread_pool.cpp - src/utils/time.cpp - src/utils/String.cpp - src/win32/net_win32.cpp - src/client_sample.cpp - src/eventqueueloop.cpp - src/net.cpp - src/PackageDataSink.cpp - src/sample.cpp - CMakeLists.txt - Makefile.bsd - Makefile.linux include/utils/exendian.h include/rest/HttpServer.h include/rest/HttpRequest.h include/rest/HttpResponse.h include/rest/HttpClient.h include/rest/HttpDataSink.h include/rest/HttpContext.h src/http/HttpResponse.cpp src/http/HttpRequest.cpp include/rest/HttpConnection.h src/http/HttpServer.cpp src/http/HttpConnection.cpp) - -include_directories(include) -include_directories(include/bsd) -include_directories(include/epoll) -include_directories(include/iocp) -include_directories(include/kqueue) -include_directories(include/linux) -include_directories(include/template) -include_directories(include/utils) -include_directories(include/win32) - -add_executable(net_framework ${SOURCE_FILES}) diff --git a/deps/meshy/Kakefile b/deps/meshy/Kakefile deleted file mode 100755 index 8fc29a6..0000000 --- a/deps/meshy/Kakefile +++ /dev/null @@ -1,24 +0,0 @@ -project: - name: meshy - version: 0.1.0 - type: cpp - -make: - configuration: - targets: - meshy: - type: shared_library - exclude: [ "sample.cpp", "client_sample.cpp" ] - sample: - type: executable - exclude: [ "client_sample.cpp" ] - client_sample: - type: executable - exclude: [ "sample.cpp" ] - compiler: - cpp: - flags: "-std=c++11 -fPIC" - defines: [ "OS_LINUX" ] - linker: - ld: g++ - libraries: [ "pthread" ] diff --git a/deps/meshy/Makefile b/deps/meshy/Makefile deleted file mode 100755 index 76d6cad..0000000 --- a/deps/meshy/Makefile +++ /dev/null @@ -1,156 +0,0 @@ -SRC = src -INCLUDE = include -TARGET = target -BUILD = $(TARGET)/build -CC = gcc -CXX = g++ - -CXXFLAGS = -std=c++11 -I$(INCLUDE) -DOS_LINUX -g -fPIC -LDFALGS = -lpthread - -OBJECTS = $(BUILD)/PackageDataSink.o \ - $(BUILD)/EPollConnection.o \ - $(BUILD)/EPollStream.o \ - $(BUILD)/EPollClient.o \ - $(BUILD)/EPollServer.o \ - $(BUILD)/EPollLoop.o \ - $(BUILD)/eventqueueloop.o \ - $(BUILD)/net.o \ - $(BUILD)/logger.o \ - $(BUILD)/time.o \ - $(BUILD)/thread_pool.o \ - $(BUILD)/common_utils.o \ - $(BUILD)/String.o \ - $(BUILD)/HttpContext.o \ - $(BUILD)/HttpRequest.o \ - $(BUILD)/HttpResponse.o \ - $(BUILD)/HttpServer.o \ - $(BUILD)/HttpConnection.o - -OBJECTS_SAMPLE = $(BUILD)/sample.o \ - $(BUILD)/PackageDataSink.o \ - $(BUILD)/EPollConnection.o \ - $(BUILD)/EPollStream.o \ - $(BUILD)/EPollClient.o \ - $(BUILD)/EPollServer.o \ - $(BUILD)/EPollLoop.o \ - $(BUILD)/eventqueueloop.o \ - $(BUILD)/net.o \ - $(BUILD)/logger.o \ - $(BUILD)/time.o \ - $(BUILD)/thread_pool.o \ - $(BUILD)/common_utils.o \ - $(BUILD)/String.o \ - $(BUILD)/HttpContext.o \ - $(BUILD)/HttpRequest.o \ - $(BUILD)/HttpResponse.o \ - $(BUILD)/HttpServer.o \ - $(BUILD)/HttpConnection.o - -OBJECTS_CLIENT = $(BUILD)/client_sample.o \ - $(BUILD)/PackageDataSink.o \ - $(BUILD)/EPollConnection.o \ - $(BUILD)/EPollStream.o \ - $(BUILD)/EPollClient.o \ - $(BUILD)/EPollServer.o \ - $(BUILD)/EPollLoop.o \ - $(BUILD)/eventqueueloop.o \ - $(BUILD)/net.o \ - $(BUILD)/logger.o \ - $(BUILD)/time.o \ - $(BUILD)/thread_pool.o \ - $(BUILD)/common_utils.o \ - $(BUILD)/String.o \ - $(BUILD)/HttpContext.o \ - $(BUILD)/HttpRequest.o \ - $(BUILD)/HttpResponse.o \ - $(BUILD)/HttpConnection.o - -all: $(TARGET)/sample $(TARGET)/client_sample - -clean: - rm -rf $(TARGET)/* - mkdir $(BUILD) - -$(TARGET)/libmeshy.so: $(OBJECTS) - $(CXX) -o $@ $(OBJECTS) $(LDFALGS) -shared - -$(TARGET)/sample: $(OBJECTS_SAMPLE) - $(CXX) -o $@ $(OBJECTS_SAMPLE) $(LDFALGS) - -$(TARGET)/client_sample: $(OBJECTS_CLIENT) - $(CXX) -o $@ $(OBJECTS_CLIENT) $(LDFALGS) - -$(BUILD)/sample.o: $(SRC)/sample.cpp $(INCLUDE)/net.h $(INCLUDE)/eventqueue.h $(INCLUDE)/eventqueueloop.h \ - $(INCLUDE)/PackageDataSink.h $(INCLUDE)/DataSink.h $(INCLUDE)/bytearray.h \ - $(INCLUDE)/rest/HttpServer.h $(INCLUDE)/rest/HttpConnection.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/sample.cpp - -$(BUILD)/client_sample.o: $(SRC)/client_sample.cpp $(INCLUDE)/net.h $(INCLUDE)/eventqueue.h $(INCLUDE)/eventqueueloop.h \ - $(INCLUDE)/PackageDataSink.h $(INCLUDE)/DataSink.h $(INCLUDE)/bytearray.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/client_sample.cpp - -$(BUILD)/PackageDataSink.o: $(SRC)/PackageDataSink.cpp $(INCLUDE)/PackageDataSink.h \ - $(INCLUDE)/eventqueue.h $(INCLUDE)/DataSink.h $(INCLUDE)/bytearray.h \ - $(INCLUDE)/utils/thread_pool.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/PackageDataSink.cpp - -$(BUILD)/EPollServer.o: $(SRC)/epoll/EPollServer.cpp $(INCLUDE)/epoll/EPollServer.h \ - $(INCLUDE)/linux/net_linux.h \ - $(INCLUDE)/net.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/epoll/EPollServer.cpp - -$(BUILD)/EPollClient.o: $(SRC)/epoll/EPollClient.cpp $(INCLUDE)/epoll/EPollClient.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/epoll/EPollClient.cpp - -$(BUILD)/EPollConnection.o: $(SRC)/epoll/EPollConnection.cpp $(INCLUDE)/epoll/EPollConnection.h \ - $(INCLUDE)/linux/net_linux.h \ - $(INCLUDE)/net.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/epoll/EPollConnection.cpp - -$(BUILD)/EPollStream.o: $(SRC)/epoll/EPollStream.cpp $(INCLUDE)/epoll/EPollStream.h \ - $(INCLUDE)/linux/net_linux.h \ - $(INCLUDE)/net.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/epoll/EPollStream.cpp - -$(BUILD)/EPollLoop.o: $(SRC)/epoll/EPollLoop.cpp $(INCLUDE)/epoll/EPollLoop.h \ - $(INCLUDE)/loop.h $(INCLUDE)/DataSink.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/epoll/EPollLoop.cpp - -$(BUILD)/eventqueueloop.o: $(SRC)/eventqueueloop.cpp $(INCLUDE)/eventqueueloop.h \ - $(INCLUDE)/eventqueue.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/eventqueueloop.cpp - -$(BUILD)/net.o: $(SRC)/net.cpp $(INCLUDE)/net.h $(INCLUDE)/linux/net_linux.h $(INCLUDE)/bytearray.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/net.cpp - -$(BUILD)/logger.o: $(SRC)/utils/logger.cpp $(INCLUDE)/utils/logger.h \ - $(INCLUDE)/utils/concurrent_queue.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/utils/logger.cpp - -$(BUILD)/time.o: $(SRC)/utils/time.cpp $(INCLUDE)/utils/time.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/utils/time.cpp - -$(BUILD)/thread_pool.o: $(SRC)/utils/thread_pool.cpp $(INCLUDE)/utils/thread_pool.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/utils/thread_pool.cpp - -$(BUILD)/common_utils.o: $(SRC)/utils/common_utils.cpp $(INCLUDE)/utils/common_utils.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/utils/common_utils.cpp - -$(BUILD)/String.o: $(SRC)/utils/String.cpp $(INCLUDE)/utils/String.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/utils/String.cpp - -$(BUILD)/HttpContext.o: $(SRC)/http/HttpContext.cpp $(INCLUDE)/rest/HttpContext.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/http/HttpContext.cpp - -$(BUILD)/HttpRequest.o: $(SRC)/http/HttpRequest.cpp $(INCLUDE)/rest/HttpRequest.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/http/HttpRequest.cpp - -$(BUILD)/HttpResponse.o: $(SRC)/http/HttpResponse.cpp $(INCLUDE)/rest/HttpResponse.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/http/HttpResponse.cpp - -$(BUILD)/HttpServer.o: $(SRC)/http/HttpServer.cpp $(INCLUDE)/rest/HttpServer.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/http/HttpServer.cpp - -$(BUILD)/HttpConnection.o: $(SRC)/http/HttpConnection.cpp $(INCLUDE)/rest/HttpConnection.h - $(CXX) $(CXXFLAGS) -c -o $@ $(SRC)/http/HttpConnection.cpp diff --git a/deps/meshy/include/DataSink.h b/deps/meshy/include/DataSink.h index ea2157b..064ea3b 100755 --- a/deps/meshy/include/DataSink.h +++ b/deps/meshy/include/DataSink.h @@ -20,17 +20,19 @@ #ifndef NET_FRAME_DATASINK_H #define NET_FRAME_DATASINK_H -#include +#include "net.h" +#include +#include namespace meshy { - - class IStream; - class DataSink { public: - virtual int32_t OnDataIndication(IStream *stream, const char *buf, int64_t bytes) = 0; + virtual int32_t Write(IStream *stream, const char *buf, int64_t bytes) = 0; + IStream::DataHandler StreamDataHandler(IStream *stream) { + return std::bind(&DataSink::Write, this, stream, std::placeholders::_1, std::placeholders::_2); + } }; - } + #endif //NET_FRAME_DATASINK_H diff --git a/deps/meshy/include/IoLoop.h b/deps/meshy/include/IoLoop.h index a914c2a..cf189c4 100755 --- a/deps/meshy/include/IoLoop.h +++ b/deps/meshy/include/IoLoop.h @@ -16,15 +16,15 @@ * limitations under the license. */ -#pragma once - -const int MAX_EVENT_COUNT = 5000; -const int MAX_RECV_BUFF = 65535; - -#ifdef OS_WIN32 -#include "iocp/iocploop.h" -#define IoLoop IocpLoop -#elif defined(OS_LINUX) -#include "epoll/epollloop.h" -#define IoLoop EPollLoop -#endif +#pragma once + +const int MAX_EVENT_COUNT = 5000; +const int MAX_RECV_BUFF = 65535; + +#ifdef OS_WIN32 +#include "iocp/iocploop.h" +#define IoLoop IocpLoop +#elif defined(OS_LINUX) +#include "epoll/EPollLoop.h" +#define IoLoop EPollLoop +#endif diff --git a/deps/meshy/include/PackageDataSink.h b/deps/meshy/include/PackageDataSink.h index 506aa35..df11254 100755 --- a/deps/meshy/include/PackageDataSink.h +++ b/deps/meshy/include/PackageDataSink.h @@ -16,29 +16,29 @@ * limitations under the license. */ -#pragma once - -#include "DataSink.h" -#include "bytearray.h" -#include "utils/thread_pool.h" - -namespace meshy { - class EventQueue; - - class BaseEvent; - - class PackageDataSink : public DataSink { - public: - PackageDataSink(EventQueue *eventQueue); - - ~PackageDataSink(); - - int32_t OnDataIndication(IStream *stream, const char *buf, int64_t bytes) override; - - private: - EventQueue *_eventQueue; - ThreadPool *_threadPool; - ByteArray _data; - int32_t _totalSize; - }; -} \ No newline at end of file +#pragma once + +#include "DataSink.h" +#include "bytearray.h" +#include "utils/thread_pool.h" + +namespace meshy { + class EventQueue; + + class BaseEvent; + + class PackageDataSink : public DataSink { + public: + PackageDataSink(EventQueue *eventQueue); + + ~PackageDataSink(); + + int32_t Write(IStream *stream, const char *buf, int64_t bytes) override; + + private: + EventQueue *_eventQueue; + ThreadPool *_threadPool; + ByteArray _data; + int32_t _totalSize; + }; +} diff --git a/deps/meshy/include/epoll/EPollConnection.h b/deps/meshy/include/epoll/EPollConnection.h index 90547d1..9240326 100755 --- a/deps/meshy/include/epoll/EPollConnection.h +++ b/deps/meshy/include/epoll/EPollConnection.h @@ -31,8 +31,7 @@ #include "linux/net_linux.h" #include "net.h" -#include "epoll/EpollStream.h" - +#include "epoll/EPollStream.h" namespace meshy { class EPollLoop; diff --git a/deps/meshy/include/epoll/epollloop.h b/deps/meshy/include/epoll/EPollLoop.h similarity index 100% rename from deps/meshy/include/epoll/epollloop.h rename to deps/meshy/include/epoll/EPollLoop.h diff --git a/deps/meshy/include/epoll/EPollServer.h b/deps/meshy/include/epoll/EPollServer.h index e7ec5e9..dac06df 100755 --- a/deps/meshy/include/epoll/EPollServer.h +++ b/deps/meshy/include/epoll/EPollServer.h @@ -16,39 +16,38 @@ * limitations under the license. */ -#ifndef NET_FRAMEWORK_EPOLLSERVER_H -#define NET_FRAMEWORK_EPOLLSERVER_H - -#include "net.h" -#include "PackageDataSink.h" -#include "epoll/EPollConnection.h" - - -namespace meshy { - - class EPollServer : public BasicServer { - public: - EPollServer() { } - virtual ~EPollServer() { } - - int32_t Listen(const std::string& host, int32_t port, int32_t backlog = 20) override; - - void OnConnectIndication(ConnectIndicationHandler handler) { - _connectHandler = handler; - } - void OnDisconnectIndication(DisconnectIndicationHandler handler) { - _disconnectIndication = handler; - } - - EPollConnectionPtr Accept(int32_t sockfd); - - private: - int32_t _Bind(const std::string& host, int32_t port); - - DataSink* _dataSink; - ConnectIndicationHandler _connectHandler; - DisconnectIndicationHandler _disconnectIndication; - }; - -} -#endif //NET_FRAMEWORK_EPOLLSERVER_H +#ifndef NET_FRAMEWORK_EPOLLSERVER_H +#define NET_FRAMEWORK_EPOLLSERVER_H + +#include "net.h" +#include "PackageDataSink.h" +#include "epoll/EPollConnection.h" + + +namespace meshy { + class EPollServer : public BasicServer { + public: + EPollServer() { } + virtual ~EPollServer() { } + + int32_t Listen(const std::string& host, int32_t port, int32_t backlog = 20) override; + + void OnConnect(ConnectHandler handler) { + _connectHandler = handler; + } + void OnDisconnec(DisconnectHandler handler) { + _disconnectIndication = handler; + } + + EPollConnectionPtr Accept(int32_t sockfd); + + private: + int32_t _Bind(const std::string& host, int32_t port); + + DataSink* _dataSink; + ConnectHandler _connectHandler; + DisconnectHandler _disconnectIndication; + }; + +} +#endif //NET_FRAMEWORK_EPOLLSERVER_H diff --git a/deps/meshy/include/epoll/EPollStream.h b/deps/meshy/include/epoll/EPollStream.h index 998782b..e6c63b3 100755 --- a/deps/meshy/include/epoll/EPollStream.h +++ b/deps/meshy/include/epoll/EPollStream.h @@ -54,16 +54,16 @@ namespace meshy { _events = events; } - void OnDataIndication(DataIndicationHandler handler) override { + void OnData(DataHandler handler) override { _dataHandler = handler; } - DataIndicationHandler GetDataIndication() override { + DataHandler GetDataHandler() override { return _dataHandler; } private: uint32_t _events; - DataIndicationHandler _dataHandler; + DataHandler _dataHandler; }; typedef std::shared_ptr EPollStreamPtr; diff --git a/deps/meshy/include/net.h b/deps/meshy/include/net.h index 7193d90..7d09e3d 100755 --- a/deps/meshy/include/net.h +++ b/deps/meshy/include/net.h @@ -50,7 +50,7 @@ namespace meshy { #ifdef OS_WIN32 closesocket(_nativeSocket); #else - Close(_nativeSocket); + close(_nativeSocket); #endif } @@ -70,13 +70,13 @@ namespace meshy { class IStream { public: - typedef std::function DataIndicationHandler; + typedef std::function DataHandler; virtual int32_t Receive(char* buffer, int32_t bufferSize, int32_t& readSize) = 0; virtual int32_t Send(const ByteArray& byteArray) = 0; - virtual void OnDataIndication(DataIndicationHandler handler) = 0; - virtual DataIndicationHandler GetDataIndication() = 0; + virtual void OnData(DataHandler handler) = 0; + virtual DataHandler GetDataHandler() = 0; }; class IConnectable { @@ -87,14 +87,14 @@ namespace meshy { template class BasicServer : public Socket { public: - typedef std::function ConnectIndicationHandler; - typedef std::function DisconnectIndicationHandler; + typedef std::function ConnectHandler; + typedef std::function DisconnectHandler; BasicServer() { } virtual int32_t Listen(const std::string& host, int32_t port, int backlog) = 0; - virtual void OnConnectIndication(ConnectIndicationHandler handler) = 0; - virtual void OnDisconnectIndication(DisconnectIndicationHandler handler) = 0; + virtual void OnConnect(ConnectHandler handler) = 0; + virtual void OnDisconnec(DisconnectHandler handler) = 0; virtual ConnectionType Accept(int32_t listenfd) = 0; }; diff --git a/deps/meshy/include/rest/HttpConnection.h b/deps/meshy/include/rest/HttpConnection.h index c97858f..b55ed4c 100755 --- a/deps/meshy/include/rest/HttpConnection.h +++ b/deps/meshy/include/rest/HttpConnection.h @@ -32,7 +32,7 @@ namespace meshy { HttpConnection(TcpConnection* connection); - void HandleData(const char* buffer, int64_t size); + int HandleData(const char* buffer, int64_t size); void OnData(DataHandler dataHandler) { _dataHandler = dataHandler; diff --git a/deps/meshy/kake/Kakefile b/deps/meshy/kake/Kakefile new file mode 100644 index 0000000..c0b044f --- /dev/null +++ b/deps/meshy/kake/Kakefile @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = { + name: 'AuthenCoreSDK', + version: '0.1.0', + type: 'solution', + projects: [ + 'meshy/meshy', + 'meshy/sample', + 'meshy/client-sample' + ] +}; diff --git a/deps/meshy/kake/deps.kake b/deps/meshy/kake/deps.kake new file mode 100644 index 0000000..1f8aae4 --- /dev/null +++ b/deps/meshy/kake/deps.kake @@ -0,0 +1,4 @@ +'use strict'; + +module.exports = { +}; diff --git a/deps/meshy/kake/meshy/client-sample/Kakefile b/deps/meshy/kake/meshy/client-sample/Kakefile new file mode 100644 index 0000000..2ac20e7 --- /dev/null +++ b/deps/meshy/kake/meshy/client-sample/Kakefile @@ -0,0 +1,26 @@ +'use strict'; + +module.exports = { + name: 'meshy-client-sample', + version: '0.6.1', + type: 'cpp', + target: 'executable', + targetPath: '../../../target', + compiler: { + cxx: 'g++', + cxxflags: ['-std=c++11', '-fPIC'], + defines: ['OS_LINUX'], + src: [ + '../../../src/client_sample.cpp' + ], + includePaths: [ + '../../../include' + ] + }, + linker: { + ld: 'g++', + }, + dependencies: { + meshy: 'latest' + } +}; diff --git a/deps/meshy/kake/meshy/meshy/Kakefile b/deps/meshy/kake/meshy/meshy/Kakefile new file mode 100644 index 0000000..90bb4d5 --- /dev/null +++ b/deps/meshy/kake/meshy/meshy/Kakefile @@ -0,0 +1,29 @@ +'use strict'; + +module.exports = { + name: 'meshy', + version: '0.6.1', + type: 'cpp', + target: 'dynamic_library', + targetPath: '../../../target', + compiler: { + cxx: 'g++', + cxxflags: ['-std=c++11', '-fPIC'], + defines: ['OS_LINUX'], + src: [ + '../../../src/PackageDataSink.cpp', + '../../../src/eventqueueloop.cpp', + '../../../src/net.cpp', + '../../../src/epoll/', + '../../../src/utils/', + '../../../src/http/' + ], + includePaths: [ + '../../../include' + ] + }, + linker: { + ld: 'g++', + ldflags: ['-lpthread'] + } +}; diff --git a/deps/meshy/kake/meshy/sample/Kakefile b/deps/meshy/kake/meshy/sample/Kakefile new file mode 100644 index 0000000..a6468f4 --- /dev/null +++ b/deps/meshy/kake/meshy/sample/Kakefile @@ -0,0 +1,26 @@ +'use strict'; + +module.exports = { + name: 'meshy-sample', + version: '0.6.1', + type: 'cpp', + target: 'executable', + targetPath: '../../../target', + compiler: { + cxx: 'g++', + cxxflags: ['-std=c++11', '-fPIC'], + defines: ['OS_LINUX'], + src: [ + '../../../src/sample.cpp' + ], + includePaths: [ + '../../../include' + ] + }, + linker: { + ld: 'g++', + }, + dependencies: { + meshy: 'latest' + } +}; diff --git a/deps/meshy/src/PackageDataSink.cpp b/deps/meshy/src/PackageDataSink.cpp index 95dd282..22149e3 100755 --- a/deps/meshy/src/PackageDataSink.cpp +++ b/deps/meshy/src/PackageDataSink.cpp @@ -53,7 +53,7 @@ namespace meshy { } } - int32_t PackageDataSink::OnDataIndication(IStream *stream, const char *buf, int64_t bytes) { + int32_t PackageDataSink::Write(IStream *stream, const char *buf, int64_t bytes) { _data.Concat(ByteArray(buf, bytes)); // The package is Complete if (_data.size() >= _totalSize) { @@ -64,7 +64,6 @@ namespace meshy { _totalSize = 0; } - return bytes; } } diff --git a/deps/meshy/src/epoll/EPollClient.cpp b/deps/meshy/src/epoll/EPollClient.cpp index 0cae70b..4c952c8 100755 --- a/deps/meshy/src/epoll/EPollClient.cpp +++ b/deps/meshy/src/epoll/EPollClient.cpp @@ -16,88 +16,88 @@ * limitations under the license. */ -#include -#include -#include -#include "epoll/EPollClient.h" -#include "utils/common_utils.h" -#include "epoll/EpollLoop.h" - -namespace meshy { - void EPollClient::Connect(const std::string& host, int port) { - struct sockaddr_in serv_addr; - - bzero((char *) &serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = inet_addr(host.c_str()); - serv_addr.sin_port = htons(port); - - meshy::SetNonBlocking(GetNativeSocket()); - - connect(GetNativeSocket(), (struct sockaddr *) &serv_addr, sizeof(serv_addr)); - } - - EPollClientPtr EPollClient::Connect(const std::string &ip, int32_t port, DataSink* dataSink) { - int32_t clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - - // Connect - EPollClientPtr client = EPollClientPtr(new EPollClient(clientSocket)); - client->SetDataSink(dataSink); - client->Connect(ip, port); - - // TODO: Add to epoll loop - EPollLoop *ePollLoop = EPollLoop::Get(); - - client->_events = EPOLLIN | EPOLLET; - if ( ePollLoop->AddEpollEvents(client->_events, clientSocket) == -1 ) { - perror("epoll_ctl: add"); - exit(EXIT_FAILURE); - } - - ePollLoop->AddStream(client); - - return client; - } - - int32_t EPollClient::Receive(char *buffer, int32_t bufferSize, int32_t &readSize) { - readSize = 0; - int32_t nread = 0; - NativeSocketEvent ev; - - while ((nread = read(GetNativeSocket(), buffer + readSize, bufferSize - 1)) > 0) { - readSize += nread; - } - - return nread; - } - - int32_t EPollClient::Send(const meshy::ByteArray& byteArray) { - TRACE_DEBUG("EPollConnection::Send"); - - struct epoll_event ev; - NativeSocket clientSocket = GetNativeSocket(); - - if ( EPollLoop::Get()->ModifyEpollEvents(_events | EPOLLOUT, clientSocket) ) { - // TODO: MARK ERASE - TRACE_ERROR("FATAL epoll_ctl: mod failed!"); - } - - const char *buf = byteArray.data(); - int32_t size = byteArray.size(); - int32_t n = size; - - while (n > 0) { - int32_t nwrite; - nwrite = write(clientSocket, buf + size - n, n); - if (nwrite < n) { - if (nwrite == -1 && errno != EAGAIN) { - TRACE_ERROR("FATAL write data to peer failed!"); - } - break; - } - n -= nwrite; - } - - return 0; - } -} +#include +#include +#include +#include "epoll/EPollClient.h" +#include "utils/common_utils.h" +#include "epoll/EPollLoop.h" + +namespace meshy { + void EPollClient::Connect(const std::string& host, int port) { + struct sockaddr_in serv_addr; + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = inet_addr(host.c_str()); + serv_addr.sin_port = htons(port); + + meshy::SetNonBlocking(GetNativeSocket()); + + connect(GetNativeSocket(), (struct sockaddr *) &serv_addr, sizeof(serv_addr)); + } + + EPollClientPtr EPollClient::Connect(const std::string &ip, int32_t port, DataSink* dataSink) { + int32_t clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + // Connect + EPollClientPtr client = EPollClientPtr(new EPollClient(clientSocket)); + client->SetDataSink(dataSink); + client->Connect(ip, port); + + // TODO: Add to epoll loop + EPollLoop *ePollLoop = EPollLoop::Get(); + + client->_events = EPOLLIN | EPOLLET; + if ( ePollLoop->AddEpollEvents(client->_events, clientSocket) == -1 ) { + perror("epoll_ctl: add"); + exit(EXIT_FAILURE); + } + + ePollLoop->AddStream(client); + + return client; + } + + int32_t EPollClient::Receive(char *buffer, int32_t bufferSize, int32_t &readSize) { + readSize = 0; + int32_t nread = 0; + NativeSocketEvent ev; + + while ((nread = read(GetNativeSocket(), buffer + readSize, bufferSize - 1)) > 0) { + readSize += nread; + } + + return nread; + } + + int32_t EPollClient::Send(const meshy::ByteArray& byteArray) { + TRACE_DEBUG("EPollConnection::Send"); + + struct epoll_event ev; + NativeSocket clientSocket = GetNativeSocket(); + + if ( EPollLoop::Get()->ModifyEpollEvents(_events | EPOLLOUT, clientSocket) ) { + // TODO: MARK ERASE + TRACE_ERROR("FATAL epoll_ctl: mod failed!"); + } + + const char *buf = byteArray.data(); + int32_t size = byteArray.size(); + int32_t n = size; + + while (n > 0) { + int32_t nwrite; + nwrite = write(clientSocket, buf + size - n, n); + if (nwrite < n) { + if (nwrite == -1 && errno != EAGAIN) { + TRACE_ERROR("FATAL write data to peer failed!"); + } + break; + } + n -= nwrite; + } + + return 0; + } +} diff --git a/deps/meshy/src/epoll/EPollConnection.cpp b/deps/meshy/src/epoll/EPollConnection.cpp index a3feb5a..cab4a3c 100755 --- a/deps/meshy/src/epoll/EPollConnection.cpp +++ b/deps/meshy/src/epoll/EPollConnection.cpp @@ -16,10 +16,10 @@ * limitations under the license. */ -#include "epoll/EpollLoop.h" +#include "epoll/EPollLoop.h" #include "utils/logger.h" #include #include "bytearray.h" namespace meshy { -} \ No newline at end of file +} diff --git a/deps/meshy/src/epoll/EPollServer.cpp b/deps/meshy/src/epoll/EPollServer.cpp index 6da15ec..523fb2c 100755 --- a/deps/meshy/src/epoll/EPollServer.cpp +++ b/deps/meshy/src/epoll/EPollServer.cpp @@ -16,117 +16,109 @@ * limitations under the license. */ -#include "epoll/EPollServer.h" -#include "epoll/EPollLoop.h" -#include "utils/common_utils.h" -#include "utils/logger.h" -#include -#include -#include - -#ifndef DISABLE_ASSERT -#ifdef assert -#undef assert -#endif - -#define assert(x) -#endif - -namespace meshy { - int32_t EPollServer::_Bind(const std::string& host, int32_t port) { - int32_t listenfd; - if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - TRACE_ERROR("Create socket failed!"); - exit(1); - } - - SetNativeSocket(listenfd); - int32_t option = 1; - setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); - - // make socket non-blocking - meshy::SetNonBlocking(listenfd); - - NativeSocketAddress addr; - bzero(&addr, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = inet_addr(host.c_str()); - - int32_t errorCode = bind(listenfd, (struct sockaddr *) &addr, sizeof(addr)); - if (errorCode < 0) { - TRACE_ERROR("Bind socket failed!"); - assert(0); - return errorCode; - } - - return 0; - } - - int32_t EPollServer::Listen(const std::string& host, int32_t port, int32_t backlog) { - _Bind(host, port); - - int32_t listenfd = GetNativeSocket(); - - int32_t errorCode = listen(listenfd, backlog); - if (-1 == errorCode) { - TRACE_ERROR("Listen socket failed!"); - assert(0); - return errorCode; - } - - errorCode = EPollLoop::Get()->AddEpollEvents(EPOLLIN, listenfd); - - if (errorCode == -1) { - TRACE_ERROR("FATAL epoll_ctl: listen_sock!"); - assert(0); - return errorCode; - } - -<<<<<<< HEAD -======= - this->SetDataSink(dataSink); ->>>>>>> c3c3bbe5a3163254407f40c2d102dcdc4c4383c1 - EPollLoop::Get()->AddServer(listenfd, this); - } - - EPollConnectionPtr EPollServer::Accept(int32_t sockfd) { - int32_t conn_sock; - int32_t addrlen; - int32_t remote; - - int32_t listenfd = GetNativeSocket(); - while ((conn_sock = accept(listenfd, (struct sockaddr *) &remote, - (socklen_t * ) & addrlen)) > 0) { - meshy::SetNonBlocking(conn_sock); - - NativeSocketEvent ev; - ev.events = EPOLLIN | EPOLLET; - ev.data.fd = conn_sock; - - if (epoll_ctl(sockfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) { - perror("epoll_ctl: add"); - exit(EXIT_FAILURE); - } - - EPollConnectionPtr connection = std::make_shared(conn_sock); -<<<<<<< HEAD - if ( _connectHandler ) { - _connectHandler(connection.get()); - } - -======= - connection->SetDataSink(this->GetDataSink()); ->>>>>>> c3c3bbe5a3163254407f40c2d102dcdc4c4383c1 - return connection; - } // while - - if (conn_sock == -1) { - if (errno != EAGAIN && errno != ECONNABORTED - && errno != EPROTO && errno != EINTR) - perror("accept"); - } - - return EPollConnectionPtr(nullptr); - } +#include "epoll/EPollServer.h" +#include "epoll/EPollLoop.h" +#include "utils/common_utils.h" +#include "utils/logger.h" +#include +#include +#include + +#ifndef DISABLE_ASSERT +#ifdef assert +#undef assert +#endif + +#define assert(x) +#endif + +namespace meshy { + int32_t EPollServer::_Bind(const std::string& host, int32_t port) { + int32_t listenfd; + if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + TRACE_ERROR("Create socket failed!"); + exit(1); + } + + SetNativeSocket(listenfd); + int32_t option = 1; + setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); + + // make socket non-blocking + meshy::SetNonBlocking(listenfd); + + NativeSocketAddress addr; + bzero(&addr, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(host.c_str()); + + int32_t errorCode = bind(listenfd, (struct sockaddr *) &addr, sizeof(addr)); + if (errorCode < 0) { + TRACE_ERROR("Bind socket failed!"); + assert(0); + return errorCode; + } + + return 0; + } + + int32_t EPollServer::Listen(const std::string& host, int32_t port, int32_t backlog) { + _Bind(host, port); + + int32_t listenfd = GetNativeSocket(); + + int32_t errorCode = listen(listenfd, backlog); + if (-1 == errorCode) { + TRACE_ERROR("Listen socket failed!"); + assert(0); + return errorCode; + } + + errorCode = EPollLoop::Get()->AddEpollEvents(EPOLLIN, listenfd); + + if (errorCode == -1) { + TRACE_ERROR("FATAL epoll_ctl: listen_sock!"); + assert(0); + return errorCode; + } + + EPollLoop::Get()->AddServer(listenfd, this); + } + + EPollConnectionPtr EPollServer::Accept(int32_t sockfd) { + int32_t conn_sock = 0; + int32_t addrlen = 0; + int32_t remote = 0; + + int32_t listenfd = GetNativeSocket(); + while ((conn_sock = accept(listenfd, (struct sockaddr *) &remote, + (socklen_t * ) & addrlen)) > 0) { + meshy::SetNonBlocking(conn_sock); + + NativeSocketEvent ev; + ev.events = EPOLLIN | EPOLLET; + ev.data.fd = conn_sock; + + if (epoll_ctl(sockfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) { + perror("epoll_ctl: add"); + exit(EXIT_FAILURE); + } + + EPollConnectionPtr connection = std::make_shared(conn_sock); + if ( _connectHandler ) { + _connectHandler(connection.get()); + } + + return connection; + } // while + + if (conn_sock == -1) { + if (errno != EAGAIN && errno != ECONNABORTED + && errno != EPROTO && errno != EINTR) + perror("accept"); + } + + return EPollConnectionPtr(nullptr); + } } diff --git a/deps/meshy/src/epoll/EPollStream.cpp b/deps/meshy/src/epoll/EPollStream.cpp index e17a049..30c727c 100755 --- a/deps/meshy/src/epoll/EPollStream.cpp +++ b/deps/meshy/src/epoll/EPollStream.cpp @@ -22,7 +22,7 @@ #include "epoll/EPollStream.h" -#include "epoll/EpollLoop.h" +#include "epoll/EPollLoop.h" #include "utils/logger.h" #include #include "bytearray.h" diff --git a/deps/meshy/src/epoll/epollloop.cpp b/deps/meshy/src/epoll/EpollLoop.cpp similarity index 97% rename from deps/meshy/src/epoll/epollloop.cpp rename to deps/meshy/src/epoll/EpollLoop.cpp index 6614301..c6cb307 100755 --- a/deps/meshy/src/epoll/epollloop.cpp +++ b/deps/meshy/src/epoll/EpollLoop.cpp @@ -16,7 +16,7 @@ * limitations under the license. */ -#include "epoll/EpollLoop.h" +#include "epoll/EPollLoop.h" #include "utils/logger.h" #include #include @@ -171,8 +171,8 @@ namespace meshy { { TRACE_DEBUG("_Enqueue"); - if ( stream->GetDataIndication() ) { - stream->GetDataIndication()(buf, nread); + if ( stream->GetDataHandler() ) { + stream->GetDataHandler()(buf, nread); } } diff --git a/deps/meshy/src/http/HttpConnection.cpp b/deps/meshy/src/http/HttpConnection.cpp index f4c74a0..7f124c0 100755 --- a/deps/meshy/src/http/HttpConnection.cpp +++ b/deps/meshy/src/http/HttpConnection.cpp @@ -26,10 +26,10 @@ namespace meshy { _connection(connection) { std::cout << _connection << std::endl; auto tcpDataHandler = std::bind(&HttpConnection::HandleData, this, std::placeholders::_1, std::placeholders::_2); - _connection->OnDataIndication(tcpDataHandler); + _connection->OnData(tcpDataHandler); } - void HttpConnection::HandleData(const char *buffer, int64_t size) { + int HttpConnection::HandleData(const char *buffer, int64_t size) { std::cout << buffer << std::endl; std::cout << size << std::endl; std::string requestText(buffer, size); diff --git a/deps/meshy/src/http/HttpServer.cpp b/deps/meshy/src/http/HttpServer.cpp index c1a08dc..abe1885 100755 --- a/deps/meshy/src/http/HttpServer.cpp +++ b/deps/meshy/src/http/HttpServer.cpp @@ -30,7 +30,7 @@ namespace meshy { void HttpServer::Listen(const std::string &host, int port, int backlog) { _server.Listen(host, port, backlog); - _server.OnConnectIndication([this](IStream* stream) { + _server.OnConnect([this](IStream* stream) { TcpConnection* connection = dynamic_cast(stream); HttpConnection* httpConnection = new HttpConnection(connection); if ( _connectionHandler ) { diff --git a/deps/meshy/src/sample.cpp b/deps/meshy/src/sample.cpp index 0f70fe3..4256013 100755 --- a/deps/meshy/src/sample.cpp +++ b/deps/meshy/src/sample.cpp @@ -52,40 +52,37 @@ int main() { meshy::PackageDataSink dataSink(&mainEventQueue); - meshy::HttpServer server; - server.Listen("127.0.0.1", DefaultPort); - server.OnConnection([=](meshy::HttpConnection* connection) { - connection->OnRequest([connection](const meshy::HttpRequest& request) { - std::cout << "Request arrived" << std::endl; - std::cout << request.GetMethod() << std::endl; - std::cout << request.GetPath() << std::endl; - std::cout << request.GetVersion() << std::endl; +// meshy::HttpServer server; +// server.Listen("127.0.0.1", DefaultPort); +// server.OnConnection([=](meshy::HttpConnection* connection) { +// connection->OnRequest([connection](const meshy::HttpRequest& request) { +// std::cout << "Request arrived" << std::endl; +// std::cout << request.GetMethod() << std::endl; +// std::cout << request.GetPath() << std::endl; +// std::cout << request.GetVersion() << std::endl; - meshy::HttpResponse response; - response.SetVersion("HTTP/1.1"); - response.SetStatusCode(200); - response.SetStatusMessage("OK"); - response.SetContent("Hello! Sink in Thread!"); +// meshy::HttpResponse response; +// response.SetVersion("HTTP/1.1"); +// response.SetStatusCode(200); +// response.SetStatusMessage("OK"); +// response.SetContent("Hello! Sink in Thread!"); - connection->SendResponse(response); - }); +// connection->SendResponse(response); +// }); - connection->OnData([connection](const std::string& data) { - std::cout << "Data arrived" << std::endl; - std::cout << data << std::endl; - }); - }); +// connection->OnData([connection](const std::string& data) { +// std::cout << "Data arrived" << std::endl; +// std::cout << data << std::endl; +// }); +// }); - //meshy::TcpServer server; + meshy::TcpServer server; - //meshy::PackageDataSink* packageDataSink = &dataSink; - //server.Listen("127.0.0.1", DefaultPort); - //server.OnConnectIndication([=](meshy::IStream* stream) { - // std::cout << stream << std::endl; - // stream->OnDataIndication([packageDataSink, stream](const char* buf, int64_t size) mutable { - // packageDataSink->OnDataIndication(stream, buf, size); - // }); - //}); + meshy::PackageDataSink* packageDataSink = &dataSink; + server.Listen("127.0.0.1", DefaultPort); + server.OnConnect([=](meshy::IStream* stream) { + stream->OnData(packageDataSink->StreamDataHandler(stream)); + }); SampleEventQueueLoop sampleQueue(&mainEventQueue); sampleQueue.Start(); diff --git a/deps/meshy/src/utils/logger.cpp b/deps/meshy/src/utils/logger.cpp index c2b9f50..d4f181f 100755 --- a/deps/meshy/src/utils/logger.cpp +++ b/deps/meshy/src/utils/logger.cpp @@ -70,7 +70,7 @@ namespace meshy { _fileStream = new std::ofstream(); std::ios_base::openmode mode = std::ios_base::out; mode |= std::ios_base::trunc; - _fileStream->Open(fileName, mode); + _fileStream->open(fileName, mode); // Error handling if (!_fileStream->is_open()) { @@ -110,4 +110,4 @@ namespace meshy { *_fileStream << log << std::endl; } } -} \ No newline at end of file +} diff --git a/deps/meshy/target/bin/linux/x64/Release/meshy-client-sample b/deps/meshy/target/bin/linux/x64/Release/meshy-client-sample new file mode 100755 index 0000000000000000000000000000000000000000..3336ee50dcf4d80b999e2c09099765a5e985724d GIT binary patch literal 15382 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 Cxe2oX literal 0 HcmV?d00001 diff --git a/deps/meshy/target/bin/linux/x64/Release/meshy-sample b/deps/meshy/target/bin/linux/x64/Release/meshy-sample new file mode 100755 index 0000000000000000000000000000000000000000..c8a10ab646a285da2992f3ed92969e2eedcb2b92 GIT binary patch literal 22166 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 E09kai;{X5v literal 0 HcmV?d00001 diff --git a/deps/meshy/target/build/linux/x64/Release/Makefile b/deps/meshy/target/build/linux/x64/Release/Makefile new file mode 100644 index 0000000..644564c --- /dev/null +++ b/deps/meshy/target/build/linux/x64/Release/Makefile @@ -0,0 +1,12 @@ +all: + cd meshy;make + cd meshy-sample;make + cd meshy-client-sample;make +clean: + cd meshy;make clean + cd meshy-sample;make clean + cd meshy-client-sample;make clean +install: + cd meshy;make install + cd meshy-sample;make install + cd meshy-client-sample;make install \ No newline at end of file diff --git a/deps/kake/Changelog b/deps/meshy/target/build/linux/x64/Release/Makefile.deps old mode 100755 new mode 100644 similarity index 100% rename from deps/kake/Changelog rename to deps/meshy/target/build/linux/x64/Release/Makefile.deps diff --git a/deps/meshy/target/build/linux/x64/Release/meshy-client-sample/Makefile b/deps/meshy/target/build/linux/x64/Release/meshy-client-sample/Makefile new file mode 100644 index 0000000..d4c59f0 --- /dev/null +++ b/deps/meshy/target/build/linux/x64/Release/meshy-client-sample/Makefile @@ -0,0 +1,44 @@ +-include Makefile.config + +-include ../Makefile.deps + +OBJS := __-__-__-__-__-__-src-client__sample_cpp.o + +all: meshy-client-sample + +meshy-client-sample: $(OBJS) + $(LD) $(OBJS) -o meshy-client-sample -L"../meshy" -lmeshy + +install: ../../../../../bin/linux/x64/Release/meshy-client-sample + +../../../../../bin/linux/x64/Release/meshy-client-sample: meshy-client-sample + cp meshy-client-sample ../../../../../bin/linux/x64/Release + +clean: + rm -f meshy-client-sample + rm -f *.o + +__-__-__-__-__-__-src-client__sample_cpp.o: ../../../../../../src/client_sample.cpp \ + ../../../../../../include/net.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/eventqueue.h \ + ../../../../../../include/eventqueueloop.h \ + ../../../../../../include/loop.h ../../../../../../include/IoLoop.h \ + ../../../../../../include/epoll/EPollLoop.h \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/PackageDataSink.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/utils/thread_pool.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/template/utils/thread_pool.tcc \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/linux/common.h \ + ../../../../../../include/utils/logger.h + $(CXX) ../../../../../../src/client_sample.cpp -c -o $@ -I../../../../../../include -I../../../../../../include -O2 -std=c++11 -fPIC -DOS_LINUX + diff --git a/deps/meshy/target/build/linux/x64/Release/meshy-sample/Makefile b/deps/meshy/target/build/linux/x64/Release/meshy-sample/Makefile new file mode 100644 index 0000000..b4cb96b --- /dev/null +++ b/deps/meshy/target/build/linux/x64/Release/meshy-sample/Makefile @@ -0,0 +1,54 @@ +-include Makefile.config + +-include ../Makefile.deps + +OBJS := __-__-__-__-__-__-src-sample_cpp.o + +all: meshy-sample + +meshy-sample: $(OBJS) + $(LD) $(OBJS) -o meshy-sample -L"../meshy" -lmeshy + +install: ../../../../../bin/linux/x64/Release/meshy-sample + +../../../../../bin/linux/x64/Release/meshy-sample: meshy-sample + cp meshy-sample ../../../../../bin/linux/x64/Release + +clean: + rm -f meshy-sample + rm -f *.o + +__-__-__-__-__-__-src-sample_cpp.o: ../../../../../../src/sample.cpp \ + ../../../../../../include/net.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/eventqueue.h \ + ../../../../../../include/eventqueueloop.h \ + ../../../../../../include/loop.h ../../../../../../include/IoLoop.h \ + ../../../../../../include/epoll/EPollLoop.h \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/PackageDataSink.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/utils/thread_pool.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/template/utils/thread_pool.tcc \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/linux/common.h \ + ../../../../../../include/utils/logger.h \ + ../../../../../../include/Meshy.h \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/rest/HttpServer.h \ + ../../../../../../include/rest/HttpConnection.h \ + ../../../../../../include/rest/HttpRequest.h \ + ../../../../../../include/rest/HttpContext.h \ + ../../../../../../include/rest/HttpResponse.h + $(CXX) ../../../../../../src/sample.cpp -c -o $@ -I../../../../../../include -I../../../../../../include -O2 -std=c++11 -fPIC -DOS_LINUX + diff --git a/deps/meshy/target/build/linux/x64/Release/meshy/Makefile b/deps/meshy/target/build/linux/x64/Release/meshy/Makefile new file mode 100644 index 0000000..ed500b4 --- /dev/null +++ b/deps/meshy/target/build/linux/x64/Release/meshy/Makefile @@ -0,0 +1,287 @@ +-include Makefile.config + +-include ../Makefile.deps + +OBJS := __-__-__-__-__-__-src-PackageDataSink_cpp.o \ +__-__-__-__-__-__-src-eventqueueloop_cpp.o \ +__-__-__-__-__-__-src-net_cpp.o \ +__-__-__-__-__-__-src-epoll-EPollClient_cpp.o \ +__-__-__-__-__-__-src-epoll-EPollConnection_cpp.o \ +__-__-__-__-__-__-src-epoll-EPollServer_cpp.o \ +__-__-__-__-__-__-src-epoll-EPollStream_cpp.o \ +__-__-__-__-__-__-src-epoll-EpollLoop_cpp.o \ +__-__-__-__-__-__-src-utils-String_cpp.o \ +__-__-__-__-__-__-src-utils-common__utils_cpp.o \ +__-__-__-__-__-__-src-utils-logger_cpp.o \ +__-__-__-__-__-__-src-utils-thread__pool_cpp.o \ +__-__-__-__-__-__-src-utils-time_cpp.o \ +__-__-__-__-__-__-src-http-HttpConnection_cpp.o \ +__-__-__-__-__-__-src-http-HttpContext_cpp.o \ +__-__-__-__-__-__-src-http-HttpRequest_cpp.o \ +__-__-__-__-__-__-src-http-HttpResponse_cpp.o \ +__-__-__-__-__-__-src-http-HttpServer_cpp.o + +all: libmeshy.so + +libmeshy.so: $(OBJS) + $(LD) $(OBJS) -shared -o libmeshy.so -lpthread + +install: ../../../../../lib/linux/x64/Release/libmeshy.so + +../../../../../lib/linux/x64/Release/libmeshy.so: libmeshy.so + cp libmeshy.so ../../../../../lib/linux/x64/Release + +clean: + rm -f libmeshy.so + rm -f *.o + +__-__-__-__-__-__-src-PackageDataSink_cpp.o: ../../../../../../src/PackageDataSink.cpp \ + ../../../../../../include/PackageDataSink.h \ + ../../../../../../include/DataSink.h ../../../../../../include/net.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/utils/thread_pool.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/template/utils/thread_pool.tcc \ + ../../../../../../include/eventqueue.h \ + ../../../../../../include/utils/logger.h \ + ../../../../../../include/rest/HttpContext.h \ + ../../../../../../include/rest/HttpRequest.h \ + ../../../../../../include/rest/HttpResponse.h + $(CXX) ../../../../../../src/PackageDataSink.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-eventqueueloop_cpp.o: ../../../../../../src/eventqueueloop.cpp \ + ../../../../../../include/eventqueueloop.h \ + ../../../../../../include/loop.h ../../../../../../include/eventqueue.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h + $(CXX) ../../../../../../src/eventqueueloop.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-net_cpp.o: ../../../../../../src/net.cpp ../../../../../../include/net.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h + $(CXX) ../../../../../../src/net.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-epoll-EPollClient_cpp.o: ../../../../../../src/epoll/EPollClient.cpp \ + ../../../../../../include/utils/logger.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/epoll/EPollLoop.h \ + ../../../../../../include/loop.h \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/net.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/PackageDataSink.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/utils/thread_pool.h \ + ../../../../../../include/template/utils/thread_pool.tcc \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/linux/common.h \ + ../../../../../../include/utils/common_utils.h + $(CXX) ../../../../../../src/epoll/EPollClient.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-epoll-EPollConnection_cpp.o: ../../../../../../src/epoll/EPollConnection.cpp \ + ../../../../../../include/epoll/EPollLoop.h \ + ../../../../../../include/loop.h \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/net.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/PackageDataSink.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/utils/thread_pool.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/template/utils/thread_pool.tcc \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/linux/common.h \ + ../../../../../../include/utils/logger.h + $(CXX) ../../../../../../src/epoll/EPollConnection.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-epoll-EPollServer_cpp.o: ../../../../../../src/epoll/EPollServer.cpp \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/net.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/PackageDataSink.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/utils/thread_pool.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/template/utils/thread_pool.tcc \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/epoll/EPollLoop.h \ + ../../../../../../include/loop.h \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/linux/common.h \ + ../../../../../../include/utils/common_utils.h \ + ../../../../../../include/utils/logger.h + $(CXX) ../../../../../../src/epoll/EPollServer.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-epoll-EPollStream_cpp.o: ../../../../../../src/epoll/EPollStream.cpp \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/net.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/epoll/EPollLoop.h \ + ../../../../../../include/loop.h \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/PackageDataSink.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/utils/thread_pool.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/template/utils/thread_pool.tcc \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/linux/common.h \ + ../../../../../../include/utils/logger.h + $(CXX) ../../../../../../src/epoll/EPollStream.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-epoll-EpollLoop_cpp.o: ../../../../../../src/epoll/EpollLoop.cpp \ + ../../../../../../include/epoll/EPollLoop.h \ + ../../../../../../include/loop.h \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/net.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/PackageDataSink.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/utils/thread_pool.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/template/utils/thread_pool.tcc \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/linux/common.h \ + ../../../../../../include/utils/logger.h + $(CXX) ../../../../../../src/epoll/EpollLoop.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-utils-String_cpp.o: ../../../../../../src/utils/String.cpp \ + ../../../../../../include/utils/String.h + $(CXX) ../../../../../../src/utils/String.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-utils-common__utils_cpp.o: ../../../../../../src/utils/common_utils.cpp \ + ../../../../../../include/utils/common_utils.h + $(CXX) ../../../../../../src/utils/common_utils.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-utils-logger_cpp.o: ../../../../../../src/utils/logger.cpp \ + ../../../../../../include/utils/logger.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/utils/time.h + $(CXX) ../../../../../../src/utils/logger.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-utils-thread__pool_cpp.o: ../../../../../../src/utils/thread_pool.cpp \ + ../../../../../../include/utils/thread_pool.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/template/utils/thread_pool.tcc \ + ../../../../../../include/utils/logger.h + $(CXX) ../../../../../../src/utils/thread_pool.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-utils-time_cpp.o: ../../../../../../src/utils/time.cpp \ + ../../../../../../include/utils/time.h + $(CXX) ../../../../../../src/utils/time.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-http-HttpConnection_cpp.o: ../../../../../../src/http/HttpConnection.cpp \ + ../../../../../../include/Meshy.h ../../../../../../include/IoLoop.h \ + ../../../../../../include/epoll/EPollLoop.h \ + ../../../../../../include/loop.h \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/net.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/PackageDataSink.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/utils/thread_pool.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/template/utils/thread_pool.tcc \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/linux/common.h \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/rest/HttpConnection.h \ + ../../../../../../include/rest/HttpRequest.h \ + ../../../../../../include/rest/HttpContext.h \ + ../../../../../../include/rest/HttpResponse.h + $(CXX) ../../../../../../src/http/HttpConnection.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-http-HttpContext_cpp.o: ../../../../../../src/http/HttpContext.cpp \ + ../../../../../../include/rest/HttpContext.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/utils/String.h + $(CXX) ../../../../../../src/http/HttpContext.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-http-HttpRequest_cpp.o: ../../../../../../src/http/HttpRequest.cpp \ + ../../../../../../include/rest/HttpRequest.h \ + ../../../../../../include/rest/HttpContext.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/utils/String.h + $(CXX) ../../../../../../src/http/HttpRequest.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-http-HttpResponse_cpp.o: ../../../../../../src/http/HttpResponse.cpp \ + ../../../../../../include/rest/HttpResponse.h \ + ../../../../../../include/rest/HttpContext.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/utils/String.h + $(CXX) ../../../../../../src/http/HttpResponse.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + +__-__-__-__-__-__-src-http-HttpServer_cpp.o: ../../../../../../src/http/HttpServer.cpp \ + ../../../../../../include/rest/HttpServer.h \ + ../../../../../../include/Meshy.h ../../../../../../include/IoLoop.h \ + ../../../../../../include/epoll/EPollLoop.h \ + ../../../../../../include/loop.h \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/net.h \ + ../../../../../../include/linux/net_linux.h \ + ../../../../../../include/bytearray.h \ + ../../../../../../include/utils/exendian.h \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/PackageDataSink.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/utils/thread_pool.h \ + ../../../../../../include/utils/concurrent_queue.h \ + ../../../../../../include/template/utils/thread_pool.tcc \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/DataSink.h \ + ../../../../../../include/linux/common.h \ + ../../../../../../include/epoll/EPollClient.h \ + ../../../../../../include/epoll/EPollConnection.h \ + ../../../../../../include/epoll/EPollServer.h \ + ../../../../../../include/epoll/EPollStream.h \ + ../../../../../../include/rest/HttpConnection.h \ + ../../../../../../include/rest/HttpRequest.h \ + ../../../../../../include/rest/HttpContext.h \ + ../../../../../../include/rest/HttpResponse.h + $(CXX) ../../../../../../src/http/HttpServer.cpp -c -o $@ -I../../../../../../include -O2 -std=c++11 -fPIC -fPIC -DOS_LINUX + diff --git a/deps/meshy/target/run/linux/x64/Release/start-client.sh b/deps/meshy/target/run/linux/x64/Release/start-client.sh new file mode 100755 index 0000000..8c274ca --- /dev/null +++ b/deps/meshy/target/run/linux/x64/Release/start-client.sh @@ -0,0 +1,6 @@ +export LD_LIBRARY_PATH=./ + +cp -uv ../../../../bin/linux/x64/Release/meshy-client-sample . +cp -uv ../../../../lib/linux/x64/Release/libmeshy.so . + +./meshy-client-sample diff --git a/deps/meshy/target/run/linux/x64/Release/start-server.sh b/deps/meshy/target/run/linux/x64/Release/start-server.sh new file mode 100755 index 0000000..f3dddf5 --- /dev/null +++ b/deps/meshy/target/run/linux/x64/Release/start-server.sh @@ -0,0 +1,7 @@ +export LD_LIBRARY_PATH=./ + +cp -uv ../../../../bin/linux/x64/Release/meshy-sample . +cp -uv ../../../../bin/linux/x64/Release/meshy-client-sample . +cp -uv ../../../../lib/linux/x64/Release/libmeshy.so . + +./meshy-sample diff --git a/docs/introduction.md b/docs/introduction.md index 3eac492..242c5f8 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -1,441 +1,450 @@ -## What's Hurricane - -Hurricane is a C++ based open-source distributed real-time processing system. -Different from the batch processing system like Apache Hadoop, -Hurricane uses stream model to process data. It also supports multi-language interfaces, -such as Python, JavaScript, Java and Swift. - -We imitate the interface of Apache Storm and simplify it, -so the developer familiar with Storm can learn the Hurricane easily. - -## Get Started - -Before gaining an insight into Hurricane, we use an example program to illustrate the -usage of Hurricane. - -The example program is used to count the words of all text in data stream. - -At first, we need to define a `spout` to generate the data stream. -For the sake of briefness, the spout we defined only generate the text "Hello World". -So the remaining part of program will calculate the count of word "Hello" and "World". - -Before showng the code, we use a diagram to show the structure of program. - -###HelloWorldSpout.h - -```cpp -#pragma once - -#include "hurricane/spout/ISpout.h" - -class HelloWorldSpout : public hurricane::spout::ISpout { -public: - virtual void Prepare(std::shared_ptr outputCollector) override; - virtual void Cleanup() override; - virtual std::vector DeclareFields() override; - virtual void NextTuple() override; - -private: - std::shared_ptr _outputCollector; -}; - -``` - -The `HelloWorldSpout` is inherited from the ISpout class and override some virtual functions. -Now we explain these member functions: - -* Prepare: Hurricane will call this member function to initialize the spout task. -* Cleanup: Hurricane will call this member function to destroy the spout task. -* DeclareFields: Hurricane will call this member function to get the fields of spout. -* NextTuple: Hurricane will repeated call this member function to get the next tuple. - -Now let we have a look at the implementation of HelloWorldSpout. - -### HelloWorldSpout.cpp - -```cpp -#include "sample/wordcount/HelloWorldSpout.h" - -void HelloWorldSpout::Prepare(std::shared_ptr outputCollector) { - _outputCollector = outputCollector; -} - -void HelloWorldSpout::Cleanup() { -} - -std::vector HelloWorldSpout::DeclareFields() { - return { "sentence" }; -} - -void HelloWorldSpout::NextTuple() { - _outputCollector->Emit({ - "Hello World" - }); -} -``` - -In Prepare member function, we save the outputCollector passed by Hurricane. -We will use the output collector to emit tuples. - -In DeclareFields member function, we return a vector of string. Every element in the vector is a field name. - -In NextTuple member function, we use output collector to emit a `tuple`. -The `tuple` is an array of `Value`, which can store all the basic type variable in C++(including std::string). -The fields declared by DeclareFields is used to name the elements in tuple. - -So we know that the HelloWorldSpout will repeated Emit tuples contains string "Hello Wolrd". -It is a very simple `Spout`. - -### SplitSentenceBolt.h - -Now we have a data generator called `HelloWorldSpout`. But a system must process the data, or it is useless. - -In Hurricane, we use the `Bolt` task to process input data and generate output data. -Now we define a Bolt called `SplitSentenceBolt` to split the sentence in data stream into words. - -```cpp -#pragma once - -#include "hurricane/bolt/IBolt.h" - -class SplitSentenceBolt : public hurricane::bolt::IBolt { -public: - virtual void Prepare(std::shared_ptr outputCollector) override; - virtual void Cleanup() override; - virtual std::vector DeclareFields() override; - virtual void Execute(const hurricane::base::Tuple& tuple) override; - -private: - std::shared_ptr _outputCollector; -}; -``` - -The SplitSentenceBolt is inherited from class `IBolt`, which is the base class of all Bolts. -And the interface of Bolt is similiar to the interface of Spout. It has Prepare, Cleanup and DeclareFields. - -But the last member function of Bolt is `Execute`. -Hurrican will call this function when accept a tuple from other spouts or bolts. -We will put all data processing code int this member function. - -### SplitSentenctBolt.cpp - -Now we will show the implementation of SplitSentenctBolt. - -```cpp -#include "sample/wordcount/SplitSentenceBolt.h" -#include "hurricane/util/StringUtil.h" - -void SplitSentenceBolt::Prepare(std::shared_ptr outputCollector) { - _outputCollector = outputCollector; -} - -void SplitSentenceBolt::Cleanup() { -} - -std::vector SplitSentenceBolt::DeclareFields() { - return{ "word" }; -} - -void SplitSentenceBolt::Execute(const hurricane::base::Tuple& tuple) { - std::string sentence = tuple[0].ToString(); - std::vector words = SplitString(sentence, ' '); - - for ( const std::string& word : words ) { - _outputCollector->Emit({ word }); - } -} -``` - -In DeclareFields function, we return one field called word. - -In Execute function, we get the first elment of tuple and convert it to sting. -Then we call SplitSentenct to split the sentence into words. -At last, we ergodic words and emit every word as an individual tuple. - -So this Bolt will split the sentence from other task, and emit many tuples. -Every tuple contains a word. - -### WordCountBolt.h - -After get all words in the sentences, we need to count all the words. -The `WordCountBolt` class is defined to finish the counting task. - -The definition of WordCountBolt: - -```cpp -#pragma once - -#include "hurricane/bolt/IBolt.h" - -#include -#include -#include - -class WordCountBolt : public hurricane::bolt::IBolt { -public: - virtual void Prepare(std::shared_ptr outputCollector) override; - virtual void Cleanup() override; - virtual std::vector DeclareFields() override; - virtual void Execute(const hurricane::base::Tuple& tuple) override; - -private: - std::shared_ptr _outputCollector; - std::map _wordCounts; -}; -``` - -The WordCountBolt use a map to store count of words. - -### WordCountBolt.cpp - -```cpp -#include "sample/wordcount/WordCountBolt.h" -#include "hurricane/util/StringUtil.h" - -void WordCountBolt::Prepare(std::shared_ptr outputCollector) { - _outputCollector = outputCollector; -} - -void WordCountBolt::Cleanup() { -} - -std::vector WordCountBolt::DeclareFields() { - return{ "word", "count" }; -} - -void WordCountBolt::Execute(const hurricane::base::Tuple& tuple) { - std::string word = tuple[0].ToString(); - - auto wordCountIterator = _wordCounts.find(word); - if ( wordCountIterator == _wordCounts.end() ) { - _wordCounts.insert({ word, 0 }); - wordCountIterator = _wordCounts.find(word); - } - - wordCountIterator->second ++; - - _outputCollector->Emit({ word, wordCountIterator->second }); -} -``` - -In function `DeclareFields`, we define two fields. -The first field is word and the second field is the count of word. - -In function `Execute`, we get the first element of tuple and convert it into string. - -Then we find a record of the word. If record does not exist, we initialize the record with count 0. - -Then we increment the count of word and emit the word and its count. - -### WordCountTopology.cpp - -We have compelete all task components of `Word Count`. -Then we need to create a `Topology` to combine these components. - -```cpp -#include "sample/wordcount/WordCountTopology.h" -#include "sample/wordcount/HelloWorldSpout.h" -#include "sample/wordcount/SplitSentenceBolt.h" -#include "sample/wordcount/WordCountBolt.h" - -#include "hurricane/topology/Topology.h" - -hurricane::topology::Topology* GetTopology() { - hurricane::topology::Topology* topology = new hurricane::topology::Topology("word-count-topology"); - - topology->SetSpout("hello-world-spout", new HelloWorldSpout) - .ParallismHint(1); - - topology->SetBolt("split-sentence-bolt", new SplitSentenceBolt) - .GlobalGroup("hello-world-spout") - .ParallismHint(1); - - topology->SetBolt("word-count-bolt", new WordCountBolt) - .FieldGroup("split-sentence-bolt", "word") - .ParallismHint(2); - - return topology; -} -``` - -The function name must be `GetTopology`. - -In this function, we use the constructor of Topology to create a new Topology. -The parameter of constructor is the name of topology. - -Then we use SetSpout to append a spout to topology. -The first argument is the name of spout task, the second argument is an spout instance. -We use ParallismHint to set the paramllism of taks. - -Here we create a HelloWorldSpout instance and append it to topology. -The task name is `"hello-world-spout"`. - -Then we use SetBolt to append a bolt to topology. -The first argument is the name of bolt task, the second argument is an bolt instance. - -Here we create a SplitSentenceBolt instance and WordCountBolt instance, then append them into topology. -The task names are `"split-sentence-bolt"` and `"word-count-bolt"`. - -We use Global to stream the data from spout to one SplitSentenceBolt -and use Group to stream the data from SplitSentenceBolt to WordCountBolt. - -At last, we return the topology and build it as an dynamic library. -Then we can use Hurricane to load and execute the library. - -## The concepts of Hurricane - -After the example, you can have a rough idea of Hurricane. Now we illustrate some concepts of Hurricane. -If you have learned Apache, you must be familiar with these concepts. - -We discuss following concepts: - -* Topology -* Stream -* Tuple -* Spout -* Bolt -* Stream grouping - -### Topology -The logic for a realtime application is packaged into a Hurricane topology. -A Hurricane topology is analogous to a MapReduce job. -One key difference is that a MapReduce job eventually finishes, -whereas a topology runs forever. Of course, you can kill the topology manually. -A topology is a graph of spouts and bolts that are connected with stream groupings. -We have seen the structure diagram of example program. -You can treat the topology in example as the graph in this diagram. -These concepts are described below. - -### Stream -The stream is an important abstraction in Hurricane. -A stream is an unbounded sequence of tuples that is processed and created in parallel in a distributed fashion. -Streams are defined with a schema that names the fields in the stream's tuples. -Every stream is given an id when declared. Spout and bolt will create and specifying an id for stream automatically. - -### Tuple -Tuple is the data unit transferred in stream. -The spout and bolt need to use tuple to organize the data. -Tuples can contain integers, longs, shorts, characters, floats, doubles and strings. - -### Spout -A spout is a source of streams in a topology. -Generally spouts will read tuples from an external source and emit them into the topology. -Spouts can either be reliable or unreliable. -A reliable spout is capable of replaying a tuple if it failed to be processed by Hurricane, whereas an unreliable spout forgets about the tuple as soon as it is emitted. - -The main member function on spouts is NextTuple. -NextTuple either emits a new tuple into the topology or simply returns if there are no new tuples to emit. - -The other main methods on spouts are Ack and Fail. -These are called when Hurricane detects that a tuple emitted from the spout either successfully completed through the topology or failed to be completed. -Ack and Fail are only called for reliable spouts. - -### Bolt -All processing in topologies is done in bolts. -Bolts can do anything from filtering, functions, aggregations, joins, talking to databases, and more. - -Bolts can do simple stream transformations. -Doing complex stream transformations often requires multiple steps and thus multiple bolts. -For example, transforming a stream of tweets into a stream of trending images requires at least two steps: -a bolt to do a rolling count of retweets for each image, -and one or more bolts to stream out the top X images -(you can do this particular stream transformation in a more scalable way with three bolts than with two). - -The main member function in bolts is the Execute function which takes in as input a new tuple. -Bolts emit new tuples using the OutputCollector object. -Bolts must call the Ack method on the OutputCollector for every tuple they process so that Storm knows when tuples are completed -(and can eventually determine that its safe to ack the original spout tuples). -For the common case of processing an input tuple, emitting 0 or more tuples based on that tuple, and then acking the input tuple, -Hurricane provides an IBasicBolt interface which does the acking automatically. - -Its perfectly fine to launch new threads in bolts that do processing asynchronously. OutputCollector is thread-safe and can be called at any time. - -### Stream grouping -Part of defining a topology is specifying for each bolt which streams it should receive as input. -A stream grouping defines how that stream should be partitioned among the bolt's tasks. - -There are four built-in stream groupings in Hurricane: - -* Random grouping: -Tuples are randomly distributed across the bolt's tasks in a way -such that each bolt is guaranteed to get an equal number of tuples. -* Fields grouping: -The stream is partitioned by the fields specified in the grouping. -For example, if the stream is grouped by the "user-id" field, tuples with the same "user-id" will always go to the same task, but tuples with different "user-id"'s may go to different tasks. -* Global grouping: -The entire stream goes to a single one of the bolt's tasks. -Specifically, it goes to the task with the lowest id. -* Local or random grouping: -If the target bolt has one or more tasks in the same worker process, -tuples will be shuffled to just those in-process tasks. -Otherwise, this acts like a normal shuffle grouping. - -## The architecture of Hurricane -Now we illustrate the architecture of Hurricane simply. -You can gaining an insight into Hurricane throught this chapter. - -The Hurricane is a master-slave based distributed system. -The master node will control all the slave node. -If the master node is down, all system will stop, which is called `single point of failure`. -Hurricane will solve this problem in future. - -### President -President is the master node of Hurricane. A Hurricane cluster can only have one president. -It will start a `CommandServer` and wait for other node to connect to it. - -A CommandServer is a message loop based on TCP/IP protocol, -in which you can register a command and handler. -When a CommandClient send a command to the server, -it will call the registered handler to process the command. -All Command and arguments are serialized and deserialized by a simple binary protocol. - -After a slave node join the president, president will add it to the cluster metadata. - -All topology management command are sent to president. -When you startup a topology, president will find a free executor in cluster metadata -and send command to the slave node owned the executor. -President will try to distribute the computing loads on all slave node to control the load-balance. -If there is no availiable resources, president will report error. - -But now president can't rebalance the computing executors after detecting a manager disconnected. -We will solve this problem in future. - -### Manager -Manager is the slave node of Hurricane. A Hurricane cluster will have a lot of managers. - -Manager will be started and use CommandClient to send Join command to president. -After manager receive the response of president, it will startup a CommandServer -to receive command from other managers. - -Manager will send Heartbeat command to Manager in a fixed period. -If predisent find a manager have detected the manager disconnected for some periods, -it will destroy the connection from this manager. - -When receive the StartTask command, manager will create a new task instance and -deploy it into the executor specified by president. - -After receive tuple from other task, manager will send the tuple to the correct executor -and wait for next command. - -Now we do not consider the problem that executor maybe shutdown, -but it will be solved in next version. - -### Executor -Executor is a thread to execute task. - -An executor is used to execute a spout or bolt. -It has a message loop and wait the message from manager. - -When SpoutExecutor receives the StartSpout message, it will initialize the spout task -and execute NextTuple function repeated until it receive the StopSpout message. - -When BoltExecutor receives the StartBolt message, it will initialize the bolt task -and wait for ExecuteBolt message. The manager will send the ExecuteBolt message to executor -, then executor will execute the Execute function of its bolt task. -When BoltExecutor receives the StopBolt message, it will destroy the bolt task. - -### Task -Task is a spout or bolt which is executed by executor. - -Different from Apache Storm, a Hurricane executor only execute one task, -because it is simple and easy to schedule. +## What's Hurricane + +Hurricane is Hurricane is a C++ based open-source distributed real-time processing system. +Different from the batch processing system like Apache Hadoop, +Hurricane uses stream model to process data. It also supports multi-language interfaces, +such as Python, JavaScript, Java and Swift. + +We imitate the interface of Apache Storm and simplify it, +so the developer familiar with Storm can learn the Hurricane easily. + +## Get Started + +Before gaining an insight into Hurricane, we use an example program to illustrate the +usage of Hurricane. + +The example program is used to count the words of all text in data stream. + +At first, we need to define a `spout` to generate the data stream. +For the sake of briefness, the spout we defined only generate the text "Hello World". +So the remaining part of program will calculate the count of word "Hello" and "World". + +Before showng the code, we use a diagram to show the structure of program. + +![Word Count Diagram](wordcount.png) + +###HelloWorldSpout.h + +```cpp +#pragma once + +#include "hurricane/spout/ISpout.h" + +class HelloWorldSpout : public hurricane::spout::ISpout { +public: + virtual void Prepare(std::shared_ptr outputCollector) override; + virtual void Cleanup() override; + virtual std::vector DeclareFields() override; + virtual void NextTuple() override; + +private: + std::shared_ptr _outputCollector; +}; + +``` + +The `HelloWorldSpout` is inherited from the ISpout class and override some virtual functions. +Now we explain these member functions: + +* Prepare: Hurricane will call this member function to initialize the spout task. +* Cleanup: Hurricane will call this member function to destroy the spout task. +* DeclareFields: Hurricane will call this member function to get the fields of spout. +* NextTuple: Hurricane will repeated call this member function to get the next tuple. + +Now let we have a look at the implementation of HelloWorldSpout. + +### HelloWorldSpout.cpp + +```cpp +#include "sample/wordcount/HelloWorldSpout.h" + +void HelloWorldSpout::Prepare(std::shared_ptr outputCollector) { + _outputCollector = outputCollector; +} + +void HelloWorldSpout::Cleanup() { +} + +std::vector HelloWorldSpout::DeclareFields() { + return { "sentence" }; +} + +void HelloWorldSpout::NextTuple() { + _outputCollector->Emit({ + "Hello World" + }); +} +``` + +In Prepare member function, we save the outputCollector passed by Hurricane. +We will use the output collector to emit tuples. + +In DeclareFields member function, we return a vector of string. Every element in the vector is a field name. + +In NextTuple member function, we use output collector to emit a `tuple`. +The `tuple` is an array of `Value`, which can store all the basic type variable in C++(including std::string). +The fields declared by DeclareFields is used to name the elements in tuple. + +So we know that the HelloWorldSpout will repeated Emit tuples contains string "Hello Wolrd". +It is a very simple `Spout`. + +### SplitSentenceBolt.h + +Now we have a data generator called `HelloWorldSpout`. But a system must process the data, or it is useless. + +In Hurricane, we use the `Bolt` task to process input data and generate output data. +Now we define a Bolt called `SplitSentenceBolt` to split the sentence in data stream into words. + +```cpp +#pragma once + +#include "hurricane/bolt/IBolt.h" + +class SplitSentenceBolt : public hurricane::bolt::IBolt { +public: + virtual void Prepare(std::shared_ptr outputCollector) override; + virtual void Cleanup() override; + virtual std::vector DeclareFields() override; + virtual void Execute(const hurricane::base::Tuple& tuple) override; + +private: + std::shared_ptr _outputCollector; +}; +``` + +The SplitSentenceBolt is inherited from class `IBolt`, which is the base class of all Bolts. +And the interface of Bolt is similiar to the interface of Spout. It has Prepare, Cleanup and DeclareFields. + +But the last member function of Bolt is `Execute`. +Hurrican will call this function when accept a tuple from other spouts or bolts. +We will put all data processing code int this member function. + +### SplitSentenctBolt.cpp + +Now we will show the implementation of SplitSentenctBolt. + +```cpp +#include "sample/wordcount/SplitSentenceBolt.h" +#include "hurricane/util/StringUtil.h" + +void SplitSentenceBolt::Prepare(std::shared_ptr outputCollector) { + _outputCollector = outputCollector; +} + +void SplitSentenceBolt::Cleanup() { +} + +std::vector SplitSentenceBolt::DeclareFields() { + return{ "word" }; +} + +void SplitSentenceBolt::Execute(const hurricane::base::Tuple& tuple) { + std::string sentence = tuple[0].ToString(); + std::vector words = SplitString(sentence, ' '); + + for ( const std::string& word : words ) { + _outputCollector->Emit({ word }); + } +} +``` + +In DeclareFields function, we return one field called word. + +In Execute function, we get the first elment of tuple and convert it to sting. +Then we call SplitSentenct to split the sentence into words. +At last, we ergodic words and emit every word as an individual tuple. + +So this Bolt will split the sentence from other task, and emit many tuples. +Every tuple contains a word. + +### WordCountBolt.h + +After get all words in the sentences, we need to count all the words. +The `WordCountBolt` class is defined to finish the counting task. + +The definition of WordCountBolt: + +```cpp +#pragma once + +#include "hurricane/bolt/IBolt.h" + +#include +#include +#include + +class WordCountBolt : public hurricane::bolt::IBolt { +public: + virtual void Prepare(std::shared_ptr outputCollector) override; + virtual void Cleanup() override; + virtual std::vector DeclareFields() override; + virtual void Execute(const hurricane::base::Tuple& tuple) override; + +private: + std::shared_ptr _outputCollector; + std::map _wordCounts; +}; +``` + +The WordCountBolt use a map to store count of words. + +### WordCountBolt.cpp + +```cpp +#include "sample/wordcount/WordCountBolt.h" +#include "hurricane/util/StringUtil.h" + +void WordCountBolt::Prepare(std::shared_ptr outputCollector) { + _outputCollector = outputCollector; +} + +void WordCountBolt::Cleanup() { +} + +std::vector WordCountBolt::DeclareFields() { + return{ "word", "count" }; +} + +void WordCountBolt::Execute(const hurricane::base::Tuple& tuple) { + std::string word = tuple[0].ToString(); + + auto wordCountIterator = _wordCounts.find(word); + if ( wordCountIterator == _wordCounts.end() ) { + _wordCounts.insert({ word, 0 }); + wordCountIterator = _wordCounts.find(word); + } + + wordCountIterator->second ++; + + _outputCollector->Emit({ word, wordCountIterator->second }); +} +``` + +In function `DeclareFields`, we define two fields. +The first field is word and the second field is the count of word. + +In function `Execute`, we get the first element of tuple and convert it into string. + +Then we find a record of the word. If record does not exist, we initialize the record with count 0. + +Then we increment the count of word and emit the word and its count. + +### WordCountTopology.cpp + +We have compelete all task components of `Word Count`. +Then we need to create a `Topology` to combine these components. + +```cpp +#include "sample/wordcount/WordCountTopology.h" +#include "sample/wordcount/HelloWorldSpout.h" +#include "sample/wordcount/SplitSentenceBolt.h" +#include "sample/wordcount/WordCountBolt.h" + +#include "hurricane/topology/Topology.h" + +hurricane::topology::Topology* GetTopology() { + hurricane::topology::Topology* topology = new hurricane::topology::Topology("word-count-topology"); + + topology->SetSpout("hello-world-spout", new HelloWorldSpout) + .ParallismHint(1); + + topology->SetBolt("split-sentence-bolt", new SplitSentenceBolt) + .GlobalGroup("hello-world-spout") + .ParallismHint(1); + + topology->SetBolt("word-count-bolt", new WordCountBolt) + .FieldGroup("split-sentence-bolt", "word") + .ParallismHint(2); + + return topology; +} +``` + +The function name must be `GetTopology`. + +In this function, we use the constructor of Topology to create a new Topology. +The parameter of constructor is the name of topology. + +Then we use SetSpout to append a spout to topology. +The first argument is the name of spout task, the second argument is an spout instance. +We use ParallismHint to set the paramllism of taks. + +Here we create a HelloWorldSpout instance and append it to topology. +The task name is `"hello-world-spout"`. + +Then we use SetBolt to append a bolt to topology. +The first argument is the name of bolt task, the second argument is an bolt instance. + +Here we create a SplitSentenceBolt instance and WordCountBolt instance, then append them into topology. +The task names are `"split-sentence-bolt"` and `"word-count-bolt"`. + +We use Global to stream the data from spout to one SplitSentenceBolt +and use Group to stream the data from SplitSentenceBolt to WordCountBolt. + +At last, we return the topology and build it as an dynamic library. +Then we can use Hurricane to load and execute the library. + +## The concepts of Hurricane + +After the example, you can have a rough idea of Hurricane. Now we illustrate some concepts of Hurricane. +If you have learned Apache, you must be familiar with these concepts. + +We discuss following concepts: + +* Topology +* Stream +* Tuple +* Spout +* Bolt +* Stream grouping + +### Topology +The logic for a realtime application is packaged into a Hurricane topology. +A Hurricane topology is analogous to a MapReduce job. +One key difference is that a MapReduce job eventually finishes, +whereas a topology runs forever. Of course, you can kill the topology manually. +A topology is a graph of spouts and bolts that are connected with stream groupings. +We have seen the structure diagram of example program. +You can treat the topology in example as the graph in this diagram. +These concepts are described below. + +### Stream +The stream is an important abstraction in Hurricane. +A stream is an unbounded sequence of tuples that is processed and created in parallel in a distributed fashion. +Streams are defined with a schema that names the fields in the stream's tuples. +Every stream is given an id when declared. Spout and bolt will create and specifying an id for stream automatically. + +### Tuple +Tuple is the data unit transferred in stream. +The spout and bolt need to use tuple to organize the data. +Tuples can contain integers, longs, shorts, characters, floats, doubles and strings. + +### Spout +A spout is a source of streams in a topology. +Generally spouts will read tuples from an external source and emit them into the topology. +Spouts can either be reliable or unreliable. +A reliable spout is capable of replaying a tuple if it failed to be processed by Hurricane, whereas an unreliable spout forgets about the tuple as soon as it is emitted. + +The main member function on spouts is NextTuple. +NextTuple either emits a new tuple into the topology or simply returns if there are no new tuples to emit. + +The other main methods on spouts are Ack and Fail. +These are called when Hurricane detects that a tuple emitted from the spout either successfully completed through the topology or failed to be completed. +Ack and Fail are only called for reliable spouts. + +### Bolt +All processing in topologies is done in bolts. +Bolts can do anything from filtering, functions, aggregations, joins, talking to databases, and more. + +Bolts can do simple stream transformations. +Doing complex stream transformations often requires multiple steps and thus multiple bolts. +For example, transforming a stream of tweets into a stream of trending images requires at least two steps: +a bolt to do a rolling count of retweets for each image, +and one or more bolts to stream out the top X images +(you can do this particular stream transformation in a more scalable way with three bolts than with two). + +The main member function in bolts is the Execute function which takes in as input a new tuple. +Bolts emit new tuples using the OutputCollector object. +Bolts must call the Ack method on the OutputCollector for every tuple they process so that Storm knows when tuples are completed +(and can eventually determine that its safe to ack the original spout tuples). +For the common case of processing an input tuple, emitting 0 or more tuples based on that tuple, and then acking the input tuple, +Hurricane provides an IBasicBolt interface which does the acking automatically. + +Its perfectly fine to launch new threads in bolts that do processing asynchronously. OutputCollector is thread-safe and can be called at any time. + +### Stream grouping +Part of defining a topology is specifying for each bolt which streams it should receive as input. +A stream grouping defines how that stream should be partitioned among the bolt's tasks. + +There are four built-in stream groupings in Hurricane: + +* Random grouping: +Tuples are randomly distributed across the bolt's tasks in a way +such that each bolt is guaranteed to get an equal number of tuples. +* Fields grouping: +The stream is partitioned by the fields specified in the grouping. +For example, if the stream is grouped by the "user-id" field, tuples with the same "user-id" will always go to the same task, but tuples with different "user-id"'s may go to different tasks. +* Global grouping: +The entire stream goes to a single one of the bolt's tasks. +Specifically, it goes to the task with the lowest id. +* Local or random grouping: +If the target bolt has one or more tasks in the same worker process, +tuples will be shuffled to just those in-process tasks. +Otherwise, this acts like a normal shuffle grouping. + +## The architecture of Hurricane +Now we illustrate the architecture of Hurricane simply. +You can gaining an insight into Hurricane throught this chapter. + +The Hurricane is a master-slave based distributed system. +The master node will control all the slave node. +If the master node is down, all system will stop, which is called `single point of failure`. +Hurricane will solve this problem in future. + +### President +President is the master node of Hurricane. A Hurricane cluster can only have one president. +It will start a `CommandServer` and wait for other node to connect to it. + +A CommandServer is a message loop based on TCP/IP protocol, +in which you can register a command and handler. +When a CommandClient send a command to the server, +it will call the registered handler to process the command. +All Command and arguments are serialized and deserialized by a simple binary protocol. + +After a slave node join the president, president will add it to the cluster metadata. + +All topology management command are sent to president. +When you startup a topology, president will find a free executor in cluster metadata +and send command to the slave node owned the executor. +President will try to distribute the computing loads on all slave node to control the load-balance. +If there is no availiable resources, president will report error. + +But now president can't rebalance the computing executors after detecting a manager disconnected. +We will solve this problem in future. + +### Manager +Manager is the slave node of Hurricane. A Hurricane cluster will have a lot of managers. + +Manager will be started and use CommandClient to send Join command to president. +After manager receive the response of president, it will startup a CommandServer +to receive command from other managers. + +Manager will send Heartbeat command to Manager in a fixed period. +If predisent find a manager have detected the manager disconnected for some periods, +it will destroy the connection from this manager. + +When receive the StartTask command, manager will create a new task instance and +deploy it into the executor specified by president. + +After receive tuple from other task, manager will send the tuple to the correct executor +and wait for next command. + +Now we do not consider the problem that executor maybe shutdown, +but it will be solved in next version. + +### Executor +Executor is a thread to execute task. + +An executor is used to execute a spout or bolt. +It has a message loop and wait the message from manager. + +When SpoutExecutor receives the StartSpout message, it will initialize the spout task +and execute NextTuple function repeated until it receive the StopSpout message. + +When BoltExecutor receives the StartBolt message, it will initialize the bolt task +and wait for ExecuteBolt message. The manager will send the ExecuteBolt message to executor +, then executor will execute the Execute function of its bolt task. +When BoltExecutor receives the StopBolt message, it will destroy the bolt task. + +### Task +Task is a spout or bolt which is executed by executor. + +Different from Apache Storm, a Hurricane executor only execute one task, +because it is simple and easy to schedule. + +### Summary +At last, let we summarize this chapter by a architecture diagram of Hurricane. + +![Hurricane Architecture](architecture.png) + +Now we advice you to read the code of Hurricane to know more of Hurricane. \ No newline at end of file diff --git a/include/hurricane/base/BlockingQueue.h b/include/hurricane/base/BlockingQueue.h new file mode 100755 index 0000000..fe4050c --- /dev/null +++ b/include/hurricane/base/BlockingQueue.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include +#include +#include + +namespace hurricane { + namespace base { + template + class BlockingQueue { + public: + BlockingQueue() { + } + + virtual ~BlockingQueue() { + } + + void Push(const T& element) { + std::unique_lock locker(_mutex); + + _queue.push(element); + _emptyCv.notify_one(); + } + + bool Pop(T& element, int milliseconds = 0) { + std::unique_lock locker(_mutex); + + if ( !_queue.size() ) { + if ( milliseconds == 0 ) { + _emptyCv.wait(locker); + } + else { + _emptyCv.wait_for(locker, std::chrono::milliseconds(milliseconds)); + if ( !_queue.size() ) { + return false; + } + } + } + + element = _queue.front(); + _queue.pop(); + + return true; + } + + bool Peek(T& element) const { + std::unique_lock locker(_mutex); + + if ( !_queue.size() ) { + return false; + } + + element = _queue.front(); + return true; + } + + bool Empty() const { + std::unique_lock locker(_mutex); + + return _queue.size() == 0; + } + + int GetSize() const { + std::unique_lock locker(_mutex); + + return _queue.size(); + } + + private: + mutable std::mutex _mutex; + std::condition_variable _emptyCv; + std::queue _queue; + }; + } +} \ No newline at end of file diff --git a/include/hurricane/base/DataPackage.h b/include/hurricane/base/DataPackage.h index 0530d82..4586026 100755 --- a/include/hurricane/base/DataPackage.h +++ b/include/hurricane/base/DataPackage.h @@ -37,27 +37,132 @@ namespace hurricane { virtual int32_t Write(ByteArrayWriter& writer, const Variant& variant) = 0; }; - class IntWritable : public Writable { + class Int32Writable : public Writable { public: std::string GetType() const { - return "int"; + return "int32"; } int32_t Read(ByteArrayReader& reader, Variant& variant) override { int32_t intValue = reader.read(); - variant.SetIntValue(intValue); + variant.SetInt32Value(intValue); return sizeof(int32_t); } int32_t Write(ByteArrayWriter& writer, const Variant& variant) override { - int value = variant.GetIntValue(); + int32_t value = variant.GetInt32Value(); writer.write(value); return sizeof(int32_t); } }; + class Int64Writable : public Writable { + public: + std::string GetType() const { + return "int64"; + } + + int32_t Read(ByteArrayReader& reader, Variant& variant) override { + int64_t intValue = reader.read(); + variant.SetInt64Value(intValue); + + return sizeof(int64_t); + } + + int32_t Write(ByteArrayWriter& writer, const Variant& variant) override { + int64_t value = variant.GetInt64Value(); + writer.write(value); + + return sizeof(int64_t); + } + }; + + class UInt32Writable : public Writable { + public: + std::string GetType() const { + return "uint32"; + } + + int32_t Read(ByteArrayReader& reader, Variant& variant) override { + uint32_t intValue = reader.read(); + variant.SetUInt32Value(intValue); + + return sizeof(uint32_t); + } + + int32_t Write(ByteArrayWriter& writer, const Variant& variant) override { + uint32_t value = variant.GetUInt32Value(); + writer.write(value); + + return sizeof(uint32_t); + } + }; + + class UInt64Writable : public Writable { + public: + std::string GetType() const { + return "uint64"; + } + + int32_t Read(ByteArrayReader& reader, Variant& variant) override { + uint64_t intValue = reader.read(); + variant.SetUInt64Value(intValue); + + return sizeof(uint64_t); + } + + int32_t Write(ByteArrayWriter& writer, const Variant& variant) override { + uint64_t value = variant.GetUInt64Value(); + writer.write(value); + + return sizeof(uint64_t); + } + }; + + class BooleanWritable : public Writable { + public: + std::string GetType() const { + return "boolean"; + } + + int32_t Read(ByteArrayReader& reader, Variant& variant) override { + bool boolValue = reader.read(); + variant.SetBooleanValue(boolValue); + + return sizeof(bool); + } + + int32_t Write(ByteArrayWriter& writer, const Variant& variant) override { + bool value = variant.GetBooleanValue(); + writer.write(value); + + return sizeof(bool); + } + }; + + class FloatWritable : public Writable { + public: + std::string GetType() const { + return "float"; + } + + int32_t Read(ByteArrayReader& reader, Variant& variant) override { + float floatValue = reader.read(); + variant.SetFloatValue(floatValue); + + return sizeof(float); + } + + int32_t Write(ByteArrayWriter& writer, const Variant& variant) override { + float value = variant.GetFloatValue(); + writer.write(value); + + return sizeof(float); + } + }; + class StringWritable : public Writable { public: std::string GetType() const { diff --git a/include/hurricane/base/Library.h b/include/hurricane/base/Library.h new file mode 100644 index 0000000..2dace86 --- /dev/null +++ b/include/hurricane/base/Library.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#ifdef WIN32 + +#include + +typedef HMODULE LibraryHandle; + +inline LibraryHandle HurricaneLibraryLoad(const std::string& name) { + std::string path = name + ".dll"; + return LoadLibraryA(path.c_str()); +} + +#define HurricaneLibraryFree FreeLibrary + +inline DWORD HurricaneGetLibraryError() { + return GetLastError(); +} + +#else + +#include + +typedef void* LibraryHandle; +inline LibraryHandle HurricaneLibraryLoad(const std::string& name) { + std::string path("lib"); + path += name + ".so"; + LibraryHandle handle = dlopen(path.c_str(), RTLD_NOW); + if ( !handle ) { + std::cerr << dlerror() << std::endl; + exit(EXIT_FAILURE); + } + + return handle; +} +#define HurricaneLibraryFree dlclose + +#define HurricaneGetLibraryError() dlerror() + +#endif + +#ifdef __cplusplus +#include + +template +Function HurricaneLibraryGetSymbol(LibraryHandle libraryHandle, const std::string& libraryName) { +#ifdef WIN32 + return reinterpret_cast(GetProcAddress(libraryHandle, libraryName.c_str())); +#else + return reinterpret_cast(dlsym(libraryHandle, libraryName.c_str())); +#endif +} + +std::string GetLibraryPath(); + +#endif // __cplusplus diff --git a/include/hurricane/base/NetAddress.h b/include/hurricane/base/NetAddress.h index fb19b80..276f82f 100755 --- a/include/hurricane/base/NetAddress.h +++ b/include/hurricane/base/NetAddress.h @@ -19,13 +19,14 @@ #pragma once #include +#include "hurricane/base/Variant.h" namespace hurricane { namespace base { - class NetAddress { + class NetAddress : public Serializable { public: - NetAddress() : _port(0) { - } + NetAddress() : _port(0) { + } NetAddress(const std::string& host, int port) : _host(host), _port(port) { } @@ -46,6 +47,16 @@ namespace base { _port = port; } + virtual void Serialize(Variants& variants) const override { + Variant::Serialize(variants, _host); + Variant::Serialize(variants, _port); + } + + virtual void Deserialize(Variants::const_iterator& it) override { + Variant::Deserialize(it, _host); + Variant::Deserialize(it, _port); + } + private: std::string _host; int _port; diff --git a/include/hurricane/base/Serializer.h b/include/hurricane/base/Serializer.h new file mode 100644 index 0000000..171be0c --- /dev/null +++ b/include/hurricane/base/Serializer.h @@ -0,0 +1,13 @@ +#pragma once + +#include "hurricane/base/Variant.h" + +namespace hurricane { +namespace base { + +class Seiralizer { + +}; + +} +} diff --git a/include/hurricane/base/Values.h b/include/hurricane/base/Values.h index 4ded4f8..0e2e3fe 100755 --- a/include/hurricane/base/Values.h +++ b/include/hurricane/base/Values.h @@ -24,147 +24,11 @@ #include #include #include +#include "hurricane/base/Variant.h" namespace hurricane { namespace base { - - class TypeMismatchException : std::exception { - public: - TypeMismatchException(const std::string& message) : - _message(message) {} - - - const char* what() const noexcept override { - return _message.c_str(); - } - - private: - std::string _message; - }; - - class Value { - public: - enum class Type { - Boolean, - Character, - Int8, - Int16, - Int32, - Int64, - Float, - Double, - String - }; - - union InnerValue { - bool booleanValue; - char characterValue; - int8_t int8Value; - int16_t int16Value; - int32_t int32Value; - int64_t int64Value; - float floatValue; - double doubleValue; - }; - - Value(bool value) : _type(Type::Boolean) { - _value.booleanValue = value; - } - - Value(char value) : _type(Type::Character) { - _value.characterValue = value; - } - - Value(int8_t value) : _type(Type::Int8) { - _value.int8Value = value; - } - - Value(int16_t value) : _type(Type::Int16) { - _value.int16Value = value; - } - - Value(int32_t value) : _type(Type::Int32) { - _value.int32Value = value; - } - - Value(int64_t value) : _type(Type::Int64) { - _value.int64Value = value; - } - - Value(float value) : _type(Type::Float) { - _value.floatValue = value; - } - - Value(double value) : _type(Type::Double) { - _value.doubleValue = value; - } - - Value(const std::string& value) : _type(Type::String) { - _stringValue = value; - } - - Value(const char* value) : Value(std::string(value)) { - } - - bool ToBoolean() const { - if ( _type != Type::Boolean ) { - throw TypeMismatchException("The type of value is not boolean"); - } - } - - int8_t ToInt8() const { - if ( _type != Type::Int8 ) { - throw TypeMismatchException("The type of value is not int8"); - } - - return _value.int8Value; - } - - int16_t ToInt16() const { - if ( _type != Type::Int16 ) { - throw TypeMismatchException("The type of value is not int16"); - } - - return _value.int16Value; - } - - int32_t ToInt32() const { - if ( _type != Type::Int32 ) { - throw TypeMismatchException("The type of value is not int32"); - } - - return _value.int32Value; - } - - int64_t ToInt64() const { - if ( _type != Type::Int64 ) { - throw TypeMismatchException("The type of value is not int64"); - } - - return _value.int64Value; - } - - char ToCharacter() const { - if ( _type != Type::Character ) { - throw TypeMismatchException("The type of value is not character"); - } - - return _value.characterValue; - } - - const std::string& ToString() const { - if ( _type != Type::String ) { - throw TypeMismatchException("The type of value is not string"); - } - - return _stringValue; - } - - private: - Type _type; - InnerValue _value; - std::string _stringValue; - }; + typedef Variant Value; class Values : public std::vector { public: @@ -181,7 +45,7 @@ namespace hurricane { } }; - class Tuple { + class Tuple : public base::Serializable { public: Tuple() = default; Tuple(std::initializer_list values) : _values(values) { @@ -195,6 +59,14 @@ namespace hurricane { return _values[index]; } + Value& operator[](const std::string& fieldName) { + return _values[_fieldsMap->at(fieldName)]; + } + + const Value& operator[](const std::string& fieldName) const { + return _values[_fieldsMap->at(fieldName)]; + } + int GetSize() const { return _values.size(); } @@ -203,8 +75,47 @@ namespace hurricane { _values.push_back(value); } + void SetSourceTask(const std::string& sourceTask) { + _sourceTask = sourceTask; + } + + const std::string& GetSourceTask() const { + return _sourceTask; + } + + void SetDestTask(const std::string& destTask) { + _destTask = destTask; + } + + const std::string& GetDestTask() const { + return _destTask; + } + + const Values& GetValues() const { + return _values; + } + + void SetFields(const std::vector* fields) { + _fields = fields; + } + + const std::vector* GetFields() const { + return _fields; + } + + void SetFieldsMap(const std::map* fieldsMap) { + _fieldsMap = fieldsMap; + } + + void Serialize(Variants& variants) const override; + void Deserialize(Variants::const_iterator& it) override; + private: - Values _values; - }; + std::string _sourceTask; + std::string _destTask; + Values _values; + const std::vector* _fields; + const std::map* _fieldsMap; + }; } } diff --git a/include/hurricane/base/Variant.h b/include/hurricane/base/Variant.h index 3e88078..403a4f9 100755 --- a/include/hurricane/base/Variant.h +++ b/include/hurricane/base/Variant.h @@ -24,17 +24,42 @@ #include #include #include +#include +#include + + +#define VARIANT_GETTER(TypeName, CType, valueName) \ + CType Get##TypeName##Value() const { \ + if ( _type == Type::Invalid ) { \ + std::cerr << "Invalid"; \ + } \ + \ + if ( _type == Type::##TypeName ) { \ + return valueName; \ + } \ + \ + throw "Type mismatched"; \ + } namespace hurricane { namespace base { class Variant; typedef std::vector Variants; + class Serializable { + public: + virtual void Serialize(Variants& variants) const = 0; + virtual void Deserialize(Variants::const_iterator& it) = 0; + }; + class Variant { public: enum class Type { Invalid, - Integer, + Int32, + Int64, + UInt32, + UInt64, Boolean, Float, String, @@ -44,14 +69,36 @@ namespace hurricane { static std::map TypeNames; Variant() : _type(Type::Invalid) {} - Variant(int32_t intValue) : _type(Type::Integer), _intValue(intValue) {} + Variant(int32_t intValue) : _type(Type::Int32), _int32Value(intValue) {} + Variant(int64_t longValue) : _type(Type::Int64), _int64Value(longValue) {} + Variant(uint32_t intValue) : _type(Type::UInt32), _uint32Value(intValue) {} + Variant(uint64_t longValue) : _type(Type::UInt64), _uint64Value(longValue) {} + Variant(bool boolValue) : _type(Type::Boolean), _boolValue(boolValue) {} + Variant(float floatValue) : _type(Type::Float), _floatValue(floatValue) {} Variant(const std::string& stringValue) : _type(Type::String), _stringValue(stringValue) { } + Variant(const char* stringValue) : _type(Type::String), _stringValue(stringValue) { + } ~Variant() {} Variant(const Variant& variant) : _type(variant._type) { - if ( _type == Type::Integer ) { - _intValue = variant._intValue; + if ( _type == Type::Int32 ) { + _int32Value = variant._int32Value; + } + else if ( _type == Type::Int64 ) { + _int64Value = variant._int64Value; + } + else if ( _type == Type::UInt32 ) { + _uint32Value = variant._uint32Value; + } + else if ( _type == Type::UInt64 ) { + _uint64Value = variant._uint64Value; + } + else if ( _type == Type::Boolean ) { + _boolValue = variant._boolValue; + } + else if ( _type == Type::Float ) { + _floatValue = variant._floatValue; } else if ( _type == Type::String ) { _stringValue = variant._stringValue; @@ -60,8 +107,23 @@ namespace hurricane { const Variant& operator=(const Variant& variant) { _type = variant._type; - if ( _type == Type::Integer ) { - _intValue = variant._intValue; + if ( _type == Type::Int32 ) { + _int32Value = variant._int32Value; + } + else if ( _type == Type::Int64 ) { + _int64Value = variant._int64Value; + } + else if ( _type == Type::UInt32 ) { + _uint32Value = variant._uint32Value; + } + else if ( _type == Type::UInt64 ) { + _uint64Value = variant._uint64Value; + } + else if ( _type == Type::Boolean ) { + _boolValue = variant._boolValue; + } + else if ( _type == Type::Float ) { + _floatValue = variant._floatValue; } else if ( _type == Type::String ) { _stringValue = variant._stringValue; @@ -79,21 +141,124 @@ namespace hurricane { throw "Not Implemented"; } - int32_t GetIntValue() const { + int32_t GetInt32Value() const { if ( _type == Type::Invalid ) { std::cerr << "Invalid"; } - if ( _type == Type::Integer ) { - return _intValue; + if ( _type == Type::Int32 ) { + return _int32Value; } + std::cout << "Type mismatched. " << + "Expected: " << TypeNames[Type::Int32] << + ". Actually: " << TypeNames[_type] << std::endl; throw "Type mismatched"; } - void SetIntValue(int32_t value) { - _type = Type::Integer; - _intValue = value; + void SetInt32Value(int32_t value) { + _type = Type::Int32; + _int32Value = value; + } + + int64_t GetInt64Value() const { + if ( _type == Type::Invalid ) { + std::cerr << "Invalid"; + } + + if ( _type == Type::Int64 ) { + return _int64Value; + } + + std::cout << "Type mismatched. " << + "Expected: " << TypeNames[Type::Int64] << + ". Actually: " << TypeNames[_type] << std::endl; + throw "Type mismatched"; + } + + void SetInt64Value(int64_t value) { + _type = Type::Int64; + _int64Value = value; + } + + uint32_t GetUInt32Value() const { + if ( _type == Type::Invalid ) { + std::cerr << "Invalid"; + } + + if ( _type == Type::UInt32 ) { + return _uint32Value; + } + + std::cout << "Type mismatched. " << + "Expected: " << TypeNames[Type::UInt32] << + ". Actually: " << TypeNames[_type] << std::endl; + throw "Type mismatched"; + } + + void SetUInt32Value(uint32_t value) { + _type = Type::UInt32; + _uint32Value = value; + } + + uint64_t GetUInt64Value() const { + if ( _type == Type::Invalid ) { + std::cerr << "Invalid"; + } + + if ( _type == Type::UInt64 ) { + return _uint64Value; + } + + std::cout << "Type mismatched. " << + "Expected: " << TypeNames[Type::UInt64] << + ". Actually: " << TypeNames[_type] << std::endl; + throw "Type mismatched"; + } + + void SetUInt64Value(uint64_t value) { + _type = Type::UInt64; + _uint64Value = value; + } + + bool GetBooleanValue() const { + if ( _type == Type::Invalid ) { + std::cerr << "Invalid"; + } + + if ( _type == Type::Boolean ) { + return _boolValue; + } + + std::cout << "Type mismatched. " << + "Expected: " << TypeNames[Type::Boolean] << + ". Actually: " << TypeNames[_type] << std::endl; + throw "Type mismatched"; + } + + void SetBooleanValue(bool value) { + _type = Type::Boolean; + _boolValue = value; + } + + float GetFloatValue() const { + if ( _type == Type::Invalid ) { + std::cerr << "Invalid"; + } + + if ( _type == Type::Float ) { + return _floatValue; + } + + std::cout << "Type mismatched. " << + "Expected: " << TypeNames[Type::Float] << + ". Actually: " << TypeNames[_type] << std::endl; + throw "Type mismatched"; + } + + void SetFloatValue(float value) { + _type = Type::Float; + _floatValue = value; } std::string GetStringValue() const { @@ -105,6 +270,9 @@ namespace hurricane { return _stringValue; } + std::cout << "Type mismatched. " << + "Expected: " << TypeNames[Type::String] << + ". Actually: " << TypeNames[_type] << std::endl; throw "Type mismatched"; } @@ -113,51 +281,168 @@ namespace hurricane { _stringValue = value; } + static void Deserialize(Variants::const_iterator& it, Variant& value) { + value = *it; + it ++; + } + + static void Deserialize(Variants::const_iterator& it, int32_t& value) { + value = it->GetInt32Value(); + it ++; + } + + static void Deserialize(Variants::const_iterator& it, int64_t& value) { + value = it->GetInt64Value(); + it ++; + } + + static void Deserialize(Variants::const_iterator& it, uint32_t& value) { + value = it->GetUInt32Value(); + it ++; + } + + static void Deserialize(Variants::const_iterator& it, uint64_t& value) { + value = it->GetUInt64Value(); + it ++; + } + + static void Deserialize(Variants::const_iterator& it, std::string& value) { + value = it->GetStringValue(); + it ++; + } + + static void Deserialize(Variants::const_iterator& it, Serializable& value) { + value.Deserialize(it); + } + template - static Variants FromStdSet(std::set originSet) { - Variants variants; - variants.push_back({ static_cast(originSet.size()) }); - for ( const Element& element : originSet ) { - variants.push_back({ element }); + static void Deserialize(Variants::const_iterator& it, std::vector& values) { + size_t size = 0; + Deserialize(it, size); + + values.resize(size); + for ( Element& value : values ) { + Deserialize(it, value); } + } - return variants; + template + static void Deserialize(Variants::const_iterator& it, std::list& values) { + size_t size = 0; + Deserialize(it, size); + + values.resize(size); + for ( Element& value : values ) { + Deserialize(it, value); + } } template - static std::set ToStdSet(const Variants& variants) { - std::set resultSet; + static void Deserialize(Variants::const_iterator& it, std::set& values) { + size_t size = 0; + Deserialize(it, size); + + values.clear(); + for ( int i = 0; i != size; ++ i ) { + Element value; + Deserialize(it, value); + + values.insert(value); + } + } + + template + static void Deserialize(Variants::const_iterator& it, std::map& values) { + size_t size = 0; + Deserialize(it, size); + + for ( int i = 0; i != size; ++ i ) { + Key key; + Deserialize(it, key); + + Element value; + Deserialize(it, value); + + values.insert({ key, value }); + } + } - bool isFirstValue = true; - for ( const Variant& variant : variants ) { - if ( isFirstValue ) { - isFirstValue = false; - continue; - } + static void Serialize(Variants& variants, const Variant& value) { + variants.push_back(value); + } - resultSet.insert(variant.GetValue()); + static void Serialize(Variants& variants, int32_t value) { + variants.push_back(Variant(value)); + } + + static void Serialize(Variants& variants, int64_t value) { + variants.push_back(Variant(value)); + } + + static void Serialize(Variants& variants, uint32_t value) { + variants.push_back(Variant(value)); + } + + static void Serialize(Variants& variants, uint64_t value) { + variants.push_back(Variant(value)); + } + + static void Serialize(Variants& variants, bool value) { + variants.push_back(Variant(value)); + } + + static void Serialize(Variants& variants, float value) { + variants.push_back(Variant(value)); + } + + static void Serialize(Variants& variants, const std::string& value) { + variants.push_back(Variant(value)); + } + + template + static void Serialize(Variants& variants, std::vector values) { + variants.push_back(Variant(values.size())); + for ( const Element& value : values ) { + Serialize(variants, value); } + } - return resultSet; + template + static void Serialize(Variants& variants, std::list values) { + variants.push_back(Variant(values.size())); + for ( const Element& value : values ) { + Serialize(variants, value); + } } template - static std::set ToStdSet(Variants::const_iterator beginIt, Variants::const_iterator endIt) { - std::set resultSet; + static void Serialize(Variants& variants, std::set values) { + variants.push_back(Variant(values.size())); + for ( const Element& value : values ) { + Serialize(variants, value); + } + } - for ( Variants::const_iterator it = beginIt; - it != endIt; - ++ it ) { - resultSet.insert(it->GetValue()); + template + static void Serialize(Variants& variants, std::map values) { + variants.push_back(Variant(values.size())); + for ( const std::pair& value : values ) { + Serialize(variants, value.first); + Serialize(variants, value.second); } + } - return resultSet; + static void Serialize(Variants& variants, const Serializable& object) { + object.Serialize(variants); } private: Type _type; union { - int32_t _intValue; + int32_t _int32Value; + int64_t _int64Value; + uint32_t _uint32Value; + uint64_t _uint64Value; bool _boolValue; float _floatValue; }; @@ -165,7 +450,27 @@ namespace hurricane { }; template<> inline int32_t Variant::GetValue() const { - return GetIntValue(); + return GetInt32Value(); + } + + template<> inline int64_t Variant::GetValue() const { + return GetInt64Value(); + } + + template<> inline uint32_t Variant::GetValue() const { + return GetUInt32Value(); + } + + template<> inline uint64_t Variant::GetValue() const { + return GetUInt64Value(); + } + + template<> inline bool Variant::GetValue() const { + return GetBooleanValue(); + } + + template<> inline float Variant::GetValue() const { + return GetFloatValue(); } template<> inline std::string Variant::GetValue() const { diff --git a/include/hurricane/base/externc.h b/include/hurricane/base/externc.h new file mode 100644 index 0000000..5b26ea1 --- /dev/null +++ b/include/hurricane/base/externc.h @@ -0,0 +1,8 @@ +#pragma once +#ifdef __cplusplus +#define BEGIN_EXTERN_C extern "C" { +#define END_EXTERN_C } +#else +#define BEGIN_EXTERN_C +#define END_EXTERN_C +#endif diff --git a/include/hurricane/bolt/BoltDeclarer.h b/include/hurricane/bolt/BoltDeclarer.h index 02682b0..247efec 100755 --- a/include/hurricane/bolt/BoltDeclarer.h +++ b/include/hurricane/bolt/BoltDeclarer.h @@ -3,6 +3,8 @@ #include "hurricane/task/TaskDeclarer.h" #include #include +#include +#include namespace hurricane { namespace bolt { @@ -24,16 +26,9 @@ namespace hurricane { SetGroupMethod(hurricane::task::TaskDeclarer::GroupMethod::Global); return *this; - } - - BoltDeclarer& Random(const std::string& sourceTaskName) { - SetSourceTaskName(sourceTaskName); - SetGroupMethod(hurricane::task::TaskDeclarer::GroupMethod::Random); - - return *this; - } + } - BoltDeclarer& Group(const std::string& sourceTaskName, const std::string& groupField) { + BoltDeclarer& Field(const std::string& sourceTaskName, const std::string& groupField) { SetSourceTaskName(sourceTaskName); SetGroupMethod(hurricane::task::TaskDeclarer::GroupMethod::Field); SetGroupField(groupField); @@ -41,6 +36,13 @@ namespace hurricane { return *this; } + BoltDeclarer& Random(const std::string& sourceTaskName) { + SetSourceTaskName(sourceTaskName); + SetGroupMethod(hurricane::task::TaskDeclarer::GroupMethod::Random); + + return *this; + } + const std::string& GetGroupField() const { return _groupField; } @@ -48,9 +50,24 @@ namespace hurricane { void SetGroupField(const std::string& groupField) { _groupField = groupField; } + + std::shared_ptr GetBolt() const { + return _bolt; + } + + const std::vector& GetFields() const { + return _fields; + } + + const std::map& GetFieldsMap() const { + return _fieldsMap; + } + private: std::shared_ptr _bolt; std::string _groupField; + std::vector _fields; + std::map _fieldsMap; }; } -} \ No newline at end of file +} diff --git a/include/hurricane/collector/OutputCollector.h b/include/hurricane/collector/OutputCollector.h index 5e664f1..0be1c6d 100755 --- a/include/hurricane/collector/OutputCollector.h +++ b/include/hurricane/collector/OutputCollector.h @@ -1,14 +1,37 @@ #pragma once +#include + namespace hurricane { - namespace base { - class Tuple; - } - - namespace collector { - class OutputCollector { - public: - void Emit(const hurricane::base::Tuple& tuple); - }; - } -} \ No newline at end of file +namespace base { + class Tuple; +} + +namespace collector { + class OutputQueue; + + class OutputCollector { + public: + OutputCollector() : _taskIndex(-1) {} + OutputCollector(int taskIndex, const std::string& taskName, std::shared_ptr queue) : + _taskIndex(taskIndex), _taskName(taskName), _queue(queue) { + } + + void SetQueue(std::shared_ptr queue) { + _queue = queue; + } + + std::shared_ptr GetQueue() const { + return _queue; + } + + void Emit(const hurricane::base::Tuple& tuple); + + private: + int _taskIndex; + std::string _taskName; + std::shared_ptr _queue; + }; + +} +} diff --git a/include/hurricane/collector/OutputDispatcher.h b/include/hurricane/collector/OutputDispatcher.h new file mode 100644 index 0000000..0bba549 --- /dev/null +++ b/include/hurricane/collector/OutputDispatcher.h @@ -0,0 +1,99 @@ +#pragma once + +#include "hurricane/task/TaskInfo.h" +#include "hurricane/base/NetAddress.h" +#include +#include +#include +#include + +namespace hurricane { + +namespace message { +class CommandClient; +} + +namespace collector { + +class OutputQueue; +class OutputItem; +class TaskQueue; + +typedef std::pair TaskPathName; + +class DispatchTaskInfo { +private: + int _taskType; + std::vector _globalPaths; + std::vector> _randomPaths; + std::vector>> _fieldPaths; +}; + +class OutputDispatcher { +public: + typedef std::function AskFieldCallback; + + OutputDispatcher() : _selfSpoutCount(0) { + } + + OutputDispatcher(std::shared_ptr queue) : _queue(queue), _selfSpoutCount(0) { + } + + void SetQueue(std::shared_ptr queue) { + _queue = queue; + } + + std::shared_ptr GetQueue() const { + return _queue; + } + + void SetSelfAddress(const base::NetAddress& selfAddress) { + _selfAddress = selfAddress; + } + + void SetSelfSpoutCount(int selfSpoutCount) { + _selfSpoutCount = selfSpoutCount; + } + + void SetSelfTasks(std::vector> selfTasks) { + _selfTasks = selfTasks; + } + + void SetTaskInfos(const std::vector& taskInfos); + void SetNimbusClient(message::CommandClient* nimbusClient); + + void Start(); + + void SetTaskFields(const std::map*> & taskFields) { + _taskFields = taskFields; + } + + void SetTaskFieldsMap(const std::map*>& taskFieldsMap) { + _taskFieldsMap = taskFieldsMap; + } + +private: + void MainThread(); + bool ProcessPath(const task::TaskInfo& taskInfo, const task::PathInfo& path, + OutputItem* outputItem); + void SendTupleTo(OutputItem* outputItem, const task::ExecutorPosition& executorPosition); + void AskField(TaskPathName taskPathName, const std::string& fieldValue, AskFieldCallback callback); + +private: + std::shared_ptr _queue; + std::thread _thread; + base::NetAddress _selfAddress; + int _selfSpoutCount; + + std::vector _taskInfos; + + std::vector> _selfTasks; + std::shared_ptr _nimbusClient; + std::map _commandClients; + std::map> _fieldsDestinations; + std::map*> _taskFields; + std::map*> _taskFieldsMap; +}; + +} +} diff --git a/include/hurricane/collector/OutputQueue.h b/include/hurricane/collector/OutputQueue.h new file mode 100644 index 0000000..5efbc02 --- /dev/null +++ b/include/hurricane/collector/OutputQueue.h @@ -0,0 +1,37 @@ +#pragma once + +#include "hurricane/base/BlockingQueue.h" +#include "hurricane/base/Values.h" + +namespace hurricane { +namespace collector { + +class OutputItem { +public: + OutputItem(int taskIndex, const base::Tuple& tuple, const std::string& taskName) : + _taskIndex(taskIndex), _tuple(tuple) { + _tuple.SetSourceTask(taskName); + } + + int GetTaskIndex() const { + return _taskIndex; + } + + base::Tuple& GetTuple() { + return _tuple; + } + + const base::Tuple& GetTuple() const { + return _tuple; + } + +private: + int _taskIndex; + base::Tuple _tuple; +}; + +class OutputQueue : public base::BlockingQueue { +}; + +} +} diff --git a/include/hurricane/collector/TaskQueue.h b/include/hurricane/collector/TaskQueue.h new file mode 100644 index 0000000..bf84b2c --- /dev/null +++ b/include/hurricane/collector/TaskQueue.h @@ -0,0 +1,32 @@ +#pragma once + +#include "hurricane/base/BlockingQueue.h" +#include "hurricane/base/Values.h" + +namespace hurricane { +namespace collector { + +class TaskItem { +public: + TaskItem(int taskIndex, const base::Tuple& tuple) : + _taskIndex(taskIndex), _tuple(tuple) { + } + + int GetTaskIndex() const { + return _taskIndex; + } + + const base::Tuple& GetTuple() const { + return _tuple; + } + +private: + int _taskIndex; + base::Tuple _tuple; +}; + +class TaskQueue : public base::BlockingQueue { +}; + +} +} diff --git a/include/hurricane/message/Command.h b/include/hurricane/message/Command.h index 8048531..5dedbb9 100755 --- a/include/hurricane/message/Command.h +++ b/include/hurricane/message/Command.h @@ -1,8 +1,8 @@ #pragma once -#include -#include -#include "hurricane/base/ByteArray.h" +#include +#include +#include "hurricane/base/ByteArray.h" #include "hurricane/base/Variant.h" namespace hurricane { @@ -12,7 +12,11 @@ namespace hurricane { struct Type { enum { Invalid = 0, - Join + Join, + Heartbeat, + SyncMetadata, + SendTuple, + AskField }; }; @@ -57,7 +61,7 @@ namespace hurricane { hurricane::base::ByteArray Serialize() const; private: - int32_t _type; + int32_t _type; std::vector _arguments; }; @@ -97,6 +101,12 @@ namespace hurricane { return _arguments; } + void AddArguments(const std::vector& arguments) { + for ( const hurricane::base::Variant & argument : arguments ) { + _arguments.push_back(argument); + } + } + void AddArgument(const hurricane::base::Variant& argument) { _arguments.push_back(argument); } @@ -105,8 +115,8 @@ namespace hurricane { hurricane::base::ByteArray Serialize() const; private: - int32_t _status; + int32_t _status; std::vector _arguments; }; } -} \ No newline at end of file +} diff --git a/include/hurricane/message/CommandClient.h b/include/hurricane/message/CommandClient.h index 5468204..ad309fc 100755 --- a/include/hurricane/message/CommandClient.h +++ b/include/hurricane/message/CommandClient.h @@ -13,6 +13,7 @@ namespace hurricane { class CommandClient { public: + typedef std::function ConnectCallback; typedef std::function SendCommandCallback; CommandClient(hurricane::util::NetConnector* connector) : _connector(connector) { @@ -20,10 +21,18 @@ namespace hurricane { ~CommandClient(); + void Connect(ConnectCallback callback); void SendCommand(const Command& command, SendCommandCallback callback); + hurricane::util::NetConnector* GetConnector() { + return _connector; + } + + const hurricane::util::NetConnector* GetConnector() const { + return _connector; + } private: hurricane::util::NetConnector* _connector; }; } -} \ No newline at end of file +} diff --git a/include/hurricane/message/CommandServer.h b/include/hurricane/message/CommandServer.h index 829e50d..1790e09 100755 --- a/include/hurricane/message/CommandServer.h +++ b/include/hurricane/message/CommandServer.h @@ -44,13 +44,13 @@ namespace hurricane { _connectHandler = handler; } - template - void OnCommand(int32_t commandType, ObjectType* self, HandlerType handler) { - OnCommand(commandType, std::bind(handler, self, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - } - - void OnCommand(int32_t commandType, CommandHandler handler) { - _commandHandlers.insert({ commandType, handler }); + template + void OnCommand(int32_t commandType, ObjectType* self, HandlerType handler) { + OnCommand(commandType, std::bind(handler, self, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + } + + void OnCommand(int32_t commandType, CommandHandler handler) { + _commandHandlers.insert({ commandType, handler }); } void Response(hurricane::util::TcpConnection* connection, const Response& response); @@ -78,23 +78,34 @@ namespace hurricane { std::map& commandHandlers = _commandHandlers; ConnectHandler connectHandler = _connectHandler; - _listener->OnConnection([this, connectHandler, commandHandlers](std::shared_ptr connection) { - CommandServerContext* context = new CommandServerContext; - hurricane::util::TcpConnection* rawConnection = connection.get(); - - _connectHandler(context); - - connection->OnData([this, commandHandlers, context, rawConnection](const char* buffer, int32_t size) { - hurricane::base::ByteArray commandBytes(buffer, size); - Command command; - command.Deserialize(commandBytes); - - int32_t commandType = command.GetType(); - CommandHandler handler = commandHandlers.at(commandType); - Responser responser = std::bind(&CommandServer::Response, this, rawConnection, std::placeholders::_1); - - handler(context, command, responser); - }); + _listener->OnConnection([this, connectHandler, commandHandlers](std::shared_ptr connection) { + CommandServerContext* context = new CommandServerContext; + hurricane::util::TcpConnection* rawConnection = connection.get(); + + _connectHandler(context); + + connection->OnData([this, commandHandlers, context, rawConnection](const char* buffer, int32_t size) { + hurricane::base::ByteArray commandBytes(buffer, size); + Command command; + command.Deserialize(commandBytes); + + int32_t commandType = command.GetType(); + try { + CommandHandler handler = commandHandlers.at(commandType); + Responser responser = std::bind(&CommandServer::Response, this, rawConnection, std::placeholders::_1); + + handler(context, command, responser); + } + catch ( const std::exception& e ) { + std::cout << "Some errors in command handler" << std::endl; + std::cerr << e.what() << std::endl; + + Responser responser = std::bind(&CommandServer::Response, this, rawConnection, std::placeholders::_1); + + hurricane::message::Response response(hurricane::message::Response::Status::Failed); + responser(response); + } + }); }); _listener->StartListen(); @@ -106,4 +117,4 @@ namespace hurricane { connection->Send(responseBytes.data(), responseBytes.size()); } } -} \ No newline at end of file +} diff --git a/include/hurricane/message/Message.h b/include/hurricane/message/Message.h index 31b3936..44d1099 100755 --- a/include/hurricane/message/Message.h +++ b/include/hurricane/message/Message.h @@ -18,7 +18,8 @@ #pragma once -#include +#include "hurricane/base/BlockingQueue.h" +#include "hurricane/base/Variant.h" #include #include #include @@ -33,6 +34,10 @@ namespace hurricane { Message(int32_t type) : _type(type) { } + Message(int32_t type, const base::Variants& arguments) : + _type(type), _arguments(arguments) { + } + int32_t GetType() const { return _type; } @@ -41,23 +46,34 @@ namespace hurricane { _type = type; } + const base::Variants& GetArguments() const { + return _arguments; + } + + void SetArguments(const base::Variants& arguments) { + _arguments = arguments; + } + private: int32_t _type; + base::Variants _arguments; }; + class MessageQueue: public base::BlockingQueue { + }; + class MessageLoop { public: - typedef std::function MessageHandler; + typedef std::function MessageHandler; - MessageLoop() { - _threadId = GetCurrentThreadId(); + MessageLoop() { } MessageLoop(const MessageLoop&) = delete; const MessageLoop& operator=(const MessageLoop&) = delete; template - void MessageMap(int messageType, ObjectType* self, MethodType method) { + void MessageMap(int messageType, ObjectType* self, MethodType method) { MessageMap(messageType, std::bind(method, self, std::placeholders::_1)); } @@ -66,27 +82,29 @@ namespace hurricane { } void Run() { - MSG msg; + Message* msg = nullptr; - while ( GetMessage(&msg, 0, 0, 0) ) { - std::cerr << "Recived Message" << std::endl; - auto handler = _messageHandlers.find(msg.message); + while ( true ) { + _messageQueue.Pop(msg); + if ( msg ) { + auto handler = _messageHandlers.find(msg->GetType()); - if ( handler != _messageHandlers.end() ) { - handler->second((Message*)(msg.wParam)); - } + if ( handler != _messageHandlers.end() ) { + handler->second(*msg); + } - DispatchMessage(&msg); - } + delete msg; + } + } } - void PostMessage(Message* message) { - PostThreadMessage(_threadId, message->GetType(), WPARAM(message), 0); + void PostMessage(Message* message) { + _messageQueue.Push(message); } private: - std::map _messageHandlers; - int32_t _threadId; + std::map _messageHandlers; + MessageQueue _messageQueue; }; class MessageLoopManager { diff --git a/include/hurricane/service/Nimbus.h b/include/hurricane/service/Nimbus.h index 4afe333..8ce6827 100755 --- a/include/hurricane/service/Nimbus.h +++ b/include/hurricane/service/Nimbus.h @@ -14,20 +14,64 @@ namespace hurricane { class Topology; } + namespace message { + class CommandClient; + } + + namespace spout { + class SpoutDeclarer; + } + + namespace bolt { + class BoltDeclarer; + } + namespace service { + typedef std::pair TaskPathName; + class Nimbus : public hurricane::message::CommandServer { public: Nimbus(const hurricane::base::NetAddress& host); Nimbus(const hurricane::util::Configuration& configuration); void OnConnect(SupervisorContext* context); - void OnJoin(SupervisorContext* context, const hurricane::message::Command& command, + void OnJoin(SupervisorContext* context, const hurricane::message::Command& command, hurricane::message::CommandServer::Responser responser); + void OnAskField(SupervisorContext* context, const hurricane::message::Command& command, + hurricane::message::CommandServer::Responser responser); void SubmitTopology(hurricane::topology::Topology* topology); + + private: + void SendHeartbeat(const std::string supervisorId); + std::list GetAllSpoutTasks(const std::map& spoutDeclarers, hurricane::topology::Topology* topology); + void AllocateSpoutTasks(std::map nameToSpoutTasks, std::list originSpoutTasks); + std::map > AllocateSpoutTasks(std::list& originSpoutTasks); + std::list GetAllBoltTasks(hurricane::topology::Topology* topology, const std::map& boltDeclarers); + std::map > AllocateBoltTasks(std::list& originBoltTasks); + std::vector FindTask( + const std::map >& nameToBoltTasks, + const std::map >& nameToSpoutTasks, + const std::string& sourceTaskName); + std::vector FindTask( + const std::map >& nameToBoltTasks, + const std::string& sourceTaskName); + void CalculateTaskPaths( + const std::map >& nameToBoltTasks, + const std::map& boltDeclarers, + const std::map >& nameToSpoutTasks); + void ShowSupervisorMetadata(); + void ShowSupervisorTaskInfos(); + void ShowTaskInfos(const std::vector& taskInfos); + void SyncWithSupervisors(); private: hurricane::base::NetAddress _nimbusHost; std::vector _supervisors; - }; + int _supervisorCount; + std::shared_ptr _configuration; + std::map> _supervisorClients; + std::map> _fieldsCandidates; + std::map> _fieldsDestinations; + }; } -} \ No newline at end of file +} diff --git a/include/hurricane/service/Supervisor.h b/include/hurricane/service/Supervisor.h index a0b6086..8535f24 100755 --- a/include/hurricane/service/Supervisor.h +++ b/include/hurricane/service/Supervisor.h @@ -4,6 +4,7 @@ #include "hurricane/util/NetListener.h" #include "hurricane/base/NetAddress.h" #include "hurricane/service/SupervisorContext.h" +#include "hurricane/collector/OutputDispatcher.h" #include #include @@ -16,7 +17,21 @@ namespace hurricane { namespace message { class CommandClient; - } + } + + namespace task { + class SpoutExecutor; + class BoltExecutor; + } + + namespace topology { + class Topology; + } + + namespace collector { + class OutputCollector; + class TaskQueue; + } namespace service { class Supervisor : public hurricane::message::CommandServer { @@ -29,15 +44,44 @@ namespace hurricane { void JoinNimbus(JoinNimbusCallback callback); + void OnHeartbeat(SupervisorContext* context, const hurricane::message::Command& command, + hurricane::message::CommandServer::Responser responser); + void OnSyncMetadata(SupervisorContext* context, const hurricane::message::Command& command, + hurricane::message::CommandServer::Responser responser); + void OnSendTuple(SupervisorContext* context, const hurricane::message::Command& command, + hurricane::message::CommandServer::Responser responser); + + private: void InitSelfContext(); + void InitExecutors(); + void OwnSupervisorTasks(); + void ShowSupervisorMetadata(); + void ShowTaskInfos(); + void InitSpoutExecutors(); + void InitBoltExecutors(); + void InitNimbusConnector(); + void ReserveExecutors(); + void InitEvents(); + void InitTaskFieldsMap(); private: std::string _name; + std::string _host; + int _port; std::shared_ptr _supervisorConfiguration; hurricane::util::NetConnector* _nimbusConnector; hurricane::message::CommandClient* _nimbusClient; std::shared_ptr _selfContext; - }; + std::vector> _spoutExecutors; + std::vector> _boltExecutors; + std::vector> _spoutCollectors; + std::vector> _boltCollectors; + std::vector> _boltTaskQueues; + std::shared_ptr _topology; + hurricane::collector::OutputDispatcher _outputDispatcher; + std::map*> _taskFields; + std::map*> _taskFieldsMap; + }; } -} \ No newline at end of file +} diff --git a/include/hurricane/service/SupervisorContext.h b/include/hurricane/service/SupervisorContext.h index cb877ba..6163f71 100755 --- a/include/hurricane/service/SupervisorContext.h +++ b/include/hurricane/service/SupervisorContext.h @@ -1,14 +1,20 @@ #pragma once #include "hurricane/task/TaskInfo.h" + #include #include #include namespace hurricane { namespace service { - class SupervisorContext { - public: + class SupervisorContext : base::Serializable { + public: + enum class ExecutorType { + Spout, + Bolt + }; + SupervisorContext(); const std::string& GetId() const { @@ -19,6 +25,14 @@ namespace hurricane { _id = id; } + const hurricane::base::NetAddress& GetNetAddress() const { + return _netAddress; + } + + void SetNetAddress(const hurricane::base::NetAddress& netAddress) { + _netAddress = netAddress; + } + int GetSpoutCount() const { return _spoutCount; } @@ -72,6 +86,17 @@ namespace hurricane { _busySpouts.insert(spoutIndex); } + int useNextSpout() { + if ( !_freeSpouts.size() ) { + return -1; + } + + int spoutIndex = *(_freeSpouts.begin()); + useSpout(spoutIndex); + + return spoutIndex; + } + void freeSpout(int spoutIndex) { _freeSpouts.insert(spoutIndex); _busySpouts.erase(spoutIndex); @@ -82,11 +107,30 @@ namespace hurricane { _busyBolts.insert(boltIndex); } + int useNextBolt() { + if ( !_freeBolts.size() ) { + return -1; + } + + int boltIndex = *(_freeBolts.begin()); + useBolt(boltIndex); + + return boltIndex; + } + void freeBolt(int boltIndex) { _freeBolts.insert(boltIndex); _busyBolts.erase(boltIndex); } + void PrepareTaskInfos() { + _taskInfos.resize(_spoutCount + _boltCount); + } + + std::vector& GetTaskInfos() { + return _taskInfos; + } + const std::vector& GetTaskInfos() const { return _taskInfos; } @@ -99,18 +143,57 @@ namespace hurricane { return _taskInfos[index]; } - void SetTaskInfo(int index, const hurricane::task::TaskInfo& info) { + const hurricane::task::TaskInfo& GetSpoutTaskInfo(int index) const { + return _taskInfos[index]; + } + + const hurricane::task::TaskInfo& GetBoltTaskInfo(int index) const { + return _taskInfos[_spoutCount + index]; + } + + hurricane::task::TaskInfo& GetTaskInfo(int index) { + return _taskInfos[index]; + } + + hurricane::task::TaskInfo& GetSpoutTaskInfo(int index) { + return _taskInfos[index]; + } + + hurricane::task::TaskInfo& GetBoltTaskInfo(int index) { + return _taskInfos[_spoutCount + index]; + } + + void SetTaskInfo(int index, const hurricane::task::TaskInfo& info) { _taskInfos[index] = info; } - std::vector ToVariants(); - void ParseVariants(const std::vector& variants); - void ParseVariants(std::vector::const_iterator begin); - static SupervisorContext FromVariants(const std::vector& variants); - static SupervisorContext FromVariants(std::vector::const_iterator begin); + int SetSpoutTaskInfo(int index, const hurricane::task::TaskInfo& info) { + _taskInfos[index] = info; + + return index; + } + + int SetBoltTaskInfo(int index, const hurricane::task::TaskInfo& info) { + _taskInfos[_spoutCount + index] = info; + + return _spoutCount + index; + } + + int GetExecutorIndex(ExecutorType type, int subIndex) { + if ( type == ExecutorType::Spout ) { + return subIndex; + } + + return _spoutCount + subIndex; + } + + virtual void Serialize(hurricane::base::Variants& variants) const override; + virtual void Deserialize(hurricane::base::Variants::const_iterator& it) override; private: std::string _id; + hurricane::base::NetAddress _netAddress; + int _spoutCount; int _boltCount; @@ -120,7 +203,7 @@ namespace hurricane { std::set _busySpouts; std::set _busyBolts; - std::vector _taskInfos; + std::vector _taskInfos; }; } -} \ No newline at end of file +} diff --git a/include/hurricane/spout/SpoutDeclarer.h b/include/hurricane/spout/SpoutDeclarer.h index 1e15c03..dadba48 100755 --- a/include/hurricane/spout/SpoutDeclarer.h +++ b/include/hurricane/spout/SpoutDeclarer.h @@ -3,6 +3,8 @@ #include "hurricane/task/TaskDeclarer.h" #include #include +#include +#include namespace hurricane { namespace spout { @@ -19,8 +21,22 @@ namespace hurricane { return *this; } + std::shared_ptr GetSpout() const { + return _spout; + } + + const std::vector& GetFields() const { + return _fields; + } + + const std::map& GetFieldsMap() const { + return _fieldsMap; + } + private: std::shared_ptr _spout; + std::vector _fields; + std::map _fieldsMap; }; } -} \ No newline at end of file +} diff --git a/include/hurricane/task/BoltExecutor.h b/include/hurricane/task/BoltExecutor.h new file mode 100644 index 0000000..8a64939 --- /dev/null +++ b/include/hurricane/task/BoltExecutor.h @@ -0,0 +1,51 @@ +#pragma once + +#include "hurricane/task/Executor.h" +#include +#include + +namespace hurricane { + +namespace bolt { +class IBolt; +} + +namespace collector { +class TaskQueue; +} + +namespace task { + +class BoltExecutor : public Executor { +public: + BoltExecutor(); + ~BoltExecutor() {} + + void Start(); + + std::shared_ptr GetBolt() { + return _bolt; + } + + void SetBolt(bolt::IBolt* bolt) { + _bolt.reset(bolt); + } + + void SetTaskQueue(std::shared_ptr taskQueue) { + _taskQueue = taskQueue; + } + +protected: + hurricane::message::MessageLoop _loop; + +private: + void OnTuple(hurricane::message::Message& message); + void StartLoop(); + + std::thread _thread; + std::shared_ptr _bolt; + std::shared_ptr _taskQueue; +}; + +} +} diff --git a/include/hurricane/task/Executor.h b/include/hurricane/task/Executor.h new file mode 100644 index 0000000..e090ff5 --- /dev/null +++ b/include/hurricane/task/Executor.h @@ -0,0 +1,22 @@ +#pragma once + +#include "hurricane/message/Message.h" + +namespace hurricane { +namespace task { + +class Executor { +public: + struct MessageType { + enum { + OnTuple + }; + }; + + virtual ~Executor() {} + + virtual void Start() = 0; +}; + +} +} diff --git a/include/hurricane/task/SpoutExecutor.h b/include/hurricane/task/SpoutExecutor.h new file mode 100644 index 0000000..abf5f80 --- /dev/null +++ b/include/hurricane/task/SpoutExecutor.h @@ -0,0 +1,38 @@ +#pragma once + +#include "hurricane/task/Executor.h" +#include +#include + +namespace hurricane { + +namespace spout { + class ISpout; +} +namespace task { + +class SpoutExecutor : public Executor { +public: + SpoutExecutor(); + ~SpoutExecutor() {} + + void Start(); + void SetSpout(spout::ISpout* spout); + + std::shared_ptr GetSpout() { + return _spout; + } + + int GetFlowParam() const; + void SetFlowParam(int GetFlowParam); + +private: + void MainLoop(); + + std::thread _thread; + std::shared_ptr _spout; + int _flowParam; +}; + +} +} diff --git a/include/hurricane/task/TaskDeclarer.h b/include/hurricane/task/TaskDeclarer.h index 6d1f827..13fda73 100755 --- a/include/hurricane/task/TaskDeclarer.h +++ b/include/hurricane/task/TaskDeclarer.h @@ -17,9 +17,9 @@ namespace hurricane { struct GroupMethod { enum { - Global = 0, - Random = 1, - Field = 2 + Global = 0, + Field = 1, + Random = 2 }; }; @@ -84,4 +84,4 @@ namespace hurricane { int32_t _parallismHint; }; } -} \ No newline at end of file +} diff --git a/include/hurricane/task/TaskInfo.h b/include/hurricane/task/TaskInfo.h index 026e044..4c3a4db 100755 --- a/include/hurricane/task/TaskInfo.h +++ b/include/hurricane/task/TaskInfo.h @@ -4,11 +4,108 @@ #include "hurricane/base/Variant.h" #include #include +#include +#include namespace hurricane { - namespace task { - class TaskInfo { - public: + namespace service { + class SupervisorContext; + } + + namespace task { + class ExecutorPosition : public hurricane::base::Serializable { + public: + ExecutorPosition() : _executorIndex(-1) { + } + + ExecutorPosition(const hurricane::base::NetAddress& supervisor, int executorIndex) : + _supervisor(supervisor), _executorIndex(executorIndex) { + } + + const hurricane::base::NetAddress& GetSupervisor() const { + return _supervisor; + } + + void SetSupervisor(const hurricane::base::NetAddress& supervisor) { + _supervisor = supervisor; + } + + int GetExecutorIndex() const { + return _executorIndex; + } + + void SetExecutorIndex(int executorIndex) { + _executorIndex = executorIndex; + } + + virtual void Serialize(hurricane::base::Variants& variants) const override; + virtual void Deserialize(hurricane::base::Variants::const_iterator& it) override; + + private: + hurricane::base::NetAddress _supervisor; + int32_t _executorIndex; + }; + + class PathInfo : public hurricane::base::Serializable { + public: + struct GroupMethod { + enum { + Invalid = 0, + Global, + Field, + Random + }; + }; + + PathInfo() : _groupMethod(GroupMethod::Invalid) {} + + int GetGroupMethod() const { + return _groupMethod; + } + + void SetGroupMethod(int groupMethod) { + _groupMethod = groupMethod; + } + + void SetDestinationTask(const std::string& taskName) { + _destinationTask = taskName; + } + + const std::string& GetTaskName() const { + return _destinationTask; + } + + const std::string& GetFieldName() const { + return _fieldName; + } + + void SetFieldName(const std::string& fieldName) { + _fieldName = fieldName; + } + + const std::vector& GetDestinationExecutors() const { + return _destinationExecutors; + } + + void SetDestinationExecutors(const std::vector& executors) { + _destinationExecutors = executors; + } + + virtual void Serialize(hurricane::base::Variants& variants) const override; + virtual void Deserialize(hurricane::base::Variants::const_iterator& it) override; + + private: + int _groupMethod; + std::string _destinationTask; + std::string _fieldName; + std::vector _destinationExecutors; + }; + + class TaskInfo : public hurricane::base::Serializable { + public: + TaskInfo() : _supervisorContext(nullptr), _executorIndex(-1) { + } + const std::string& GetTopologyName() const { return _topologyName; } @@ -23,52 +120,49 @@ namespace hurricane { void SetTaskName(const std::string& taskName) { _taskName = taskName; - } + } - const hurricane::base::NetAddress& GetSourceSupervisor() const { - return _sourceSupervisor; - } + const std::list& GetPaths() const { + return _paths; + } - void SetSourceSupervisor(const hurricane::base::NetAddress& sourceSupervisor) { - _sourceSupervisor = sourceSupervisor; - } + void SetPaths(const std::list& paths) { + _paths = paths; + } - int GetSourceExecutorIndex() const { - return _sourceExecutorIndex; - } + void AddPath(const PathInfo& path) { + _paths.push_back(path); + } - void SetSourceExecutorIndex(int sourceExecutorIndex) { - _sourceExecutorIndex = sourceExecutorIndex; - } + const hurricane::service::SupervisorContext* GetSupervisorContext() const { + return _supervisorContext; + } - const hurricane::base::NetAddress& GetDestinationSupervisor() const { - return _destinationSupervisor; - } + hurricane::service::SupervisorContext* GetSupervisorContext() { + return _supervisorContext; + } - void SetDestinationSupervisor(const hurricane::base::NetAddress& destinationSupervisor) { - _destinationSupervisor = destinationSupervisor; - } + void SetSupervisorContext(hurricane::service::SupervisorContext* context) { + _supervisorContext = context; + } - int GetDestinationExecutorIndex() const { - return _destinationExecutorIndex; - } + int GetExecutorIndex() const { + return _executorIndex; + } - void SetDestinationExecutorIndex(int destinationExecutorIndex) { - _destinationExecutorIndex = destinationExecutorIndex; - } + void SetExecutorIndex(int executorIndex) { + _executorIndex = executorIndex; + } - std::vector ToVariants(); - void ParseVariant(const std::vector& variants); - static TaskInfo FromVariants(const std::vector& variants); + virtual void Serialize(hurricane::base::Variants& variants) const override; + virtual void Deserialize(hurricane::base::Variants::const_iterator& it) override; std::string _topologyName; - std::string _taskName; - - hurricane::base::NetAddress _sourceSupervisor; - int32_t _sourceExecutorIndex; + std::string _taskName; + std::list _paths; - hurricane::base::NetAddress _destinationSupervisor; - int32_t _destinationExecutorIndex; + hurricane::service::SupervisorContext* _supervisorContext; + int _executorIndex; }; } -} \ No newline at end of file +} diff --git a/include/hurricane/topology/TopologyLoader.h b/include/hurricane/topology/TopologyLoader.h index 43a6361..4a4fb89 100755 --- a/include/hurricane/topology/TopologyLoader.h +++ b/include/hurricane/topology/TopologyLoader.h @@ -5,14 +5,17 @@ #include #include +typedef void* LibraryHandle; + namespace hurricane { namespace topology { class Topology; class TopologyLoader { public: - static TopologyLoader& GetInstance(); + typedef Topology* (*TopologyGetter)(); + static TopologyLoader& GetInstance(); std::shared_ptr GetTopology(const std::string& name); private: @@ -21,6 +24,7 @@ namespace hurricane { const TopologyLoader& operator = (const TopologyLoader& loader) = delete; std::map> _topologies; + std::map _libraryHandles; }; } -} \ No newline at end of file +} diff --git a/include/hurricane/util/Socket.h b/include/hurricane/util/Socket.h index abe89d1..af7c395 100755 --- a/include/hurricane/util/Socket.h +++ b/include/hurricane/util/Socket.h @@ -98,7 +98,7 @@ namespace hurricane { int Send(const char* buf, size_t size) { int iSend = send(_socket, buf, size, 0); if ( iSend == SOCKET_ERROR ) - { + { throw SocketException("send() Failed"); } @@ -224,7 +224,7 @@ namespace hurricane { if ( ret == INVALID_SOCKET ) { DISPLAY_SOCKET_ERROR("connect() Failed"); - exit(-1); + throw SocketException("connect() Failed"); } } }; diff --git a/include/hurricane/util/StringUtil.h b/include/hurricane/util/StringUtil.h index f176060..eee8b7a 100755 --- a/include/hurricane/util/StringUtil.h +++ b/include/hurricane/util/StringUtil.h @@ -24,3 +24,5 @@ std::vector SplitString(const std::string& value, char seperator); std::string TrimString(const std::string& value); std::string RandomString(const std::string& candidate, int length); +std::string JoinStrings(const std::vector& words); +std::string Int2String(int value); diff --git a/include/redox.hpp b/include/redox.hpp new file mode 100644 index 0000000..4497399 --- /dev/null +++ b/include/redox.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace redox { + +class Redox { +public: + void connect(const std::string& host, int port) { + } +}; + +} diff --git a/include/sample/wordcount/HelloWorldSpout.h b/include/sample/wordcount/HelloWorldSpout.h index b3b6c04..a8ad329 100755 --- a/include/sample/wordcount/HelloWorldSpout.h +++ b/include/sample/wordcount/HelloWorldSpout.h @@ -1,6 +1,7 @@ #pragma once #include "hurricane/spout/ISpout.h" +#include class HelloWorldSpout : public hurricane::spout::ISpout { public: @@ -14,4 +15,5 @@ class HelloWorldSpout : public hurricane::spout::ISpout { private: std::shared_ptr _outputCollector; -}; \ No newline at end of file + std::vector _words; +}; diff --git a/include/sample/wordcount/SplitSentenceBolt.h b/include/sample/wordcount/SplitSentenceBolt.h index f51d9e9..f98a900 100755 --- a/include/sample/wordcount/SplitSentenceBolt.h +++ b/include/sample/wordcount/SplitSentenceBolt.h @@ -5,7 +5,7 @@ class SplitSentenceBolt : public hurricane::bolt::IBolt { public: virtual hurricane::bolt::IBolt* Clone() override { - return new SplitSentenceBolt(*this); + return new SplitSentenceBolt; } virtual void Prepare(std::shared_ptr outputCollector) override; virtual void Cleanup() override; @@ -13,5 +13,5 @@ class SplitSentenceBolt : public hurricane::bolt::IBolt { virtual void Execute(const hurricane::base::Tuple& tuple) override; private: - std::shared_ptr _outputCollector; -}; \ No newline at end of file + std::shared_ptr _outputCollector; +}; diff --git a/include/sample/wordcount/WordCountBolt.h b/include/sample/wordcount/WordCountBolt.h index 17a04c3..f03276a 100755 --- a/include/sample/wordcount/WordCountBolt.h +++ b/include/sample/wordcount/WordCountBolt.h @@ -1,6 +1,7 @@ #pragma once #include "hurricane/bolt/IBolt.h" +#include #include #include @@ -19,4 +20,5 @@ class WordCountBolt : public hurricane::bolt::IBolt { private: std::shared_ptr _outputCollector; std::map _wordCounts; -}; \ No newline at end of file + std::ofstream* _logFile; +}; diff --git a/include/sample/wordcount/WordCountTopology.h b/include/sample/wordcount/WordCountTopology.h index a0ea85e..de54d10 100755 --- a/include/sample/wordcount/WordCountTopology.h +++ b/include/sample/wordcount/WordCountTopology.h @@ -6,4 +6,8 @@ namespace hurricane { } } -hurricane::topology::Topology* GetTopology(); \ No newline at end of file +#include "hurricane/base/externc.h" + +BEGIN_EXTERN_C +hurricane::topology::Topology* GetTopology(); +END_EXTERN_C diff --git a/kake/Hurricane/Kakefile b/kake/Hurricane/Kakefile index d88c8d0..ce0ec39 100644 --- a/kake/Hurricane/Kakefile +++ b/kake/Hurricane/Kakefile @@ -6,6 +6,7 @@ module.exports = { type: 'solution', projects: [ 'nimbus', - 'supervisor' + 'supervisor', + 'wordcount' ] }; diff --git a/kake/Hurricane/deps.kake b/kake/Hurricane/deps.kake index 8eb077a..b994947 100644 --- a/kake/Hurricane/deps.kake +++ b/kake/Hurricane/deps.kake @@ -1,4 +1,9 @@ 'use strict'; module.exports = { + pthread: { + version: '1.0.0', + scope: 'system', + inputs: [ 'pthread' ] + } }; diff --git a/kake/Hurricane/nimbus/Kakefile b/kake/Hurricane/nimbus/Kakefile index fdeb573..559489c 100644 --- a/kake/Hurricane/nimbus/Kakefile +++ b/kake/Hurricane/nimbus/Kakefile @@ -19,8 +19,7 @@ module.exports = { '../../../src/hurricane/task', '../../../src/hurricane/topology', '../../../src/hurricane/util', - '../../../src/hurricane/tool/StartNimbus.cpp', - '../../../src/sample/wordcount' + '../../../src/hurricane/tool/StartNimbus.cpp' ], includePaths: [ '../../../include' @@ -28,5 +27,9 @@ module.exports = { }, linker: { ld: 'g++', + ldflags: [ '-ldl' ] + }, + dependencies: { + pthread: 'latest' } }; diff --git a/kake/Hurricane/supervisor/Kakefile b/kake/Hurricane/supervisor/Kakefile index 2c3fe14..f4591b8 100644 --- a/kake/Hurricane/supervisor/Kakefile +++ b/kake/Hurricane/supervisor/Kakefile @@ -19,8 +19,7 @@ module.exports = { '../../../src/hurricane/task', '../../../src/hurricane/topology', '../../../src/hurricane/util', - '../../../src/hurricane/tool/StartSupervisor.cpp', - '../../../src/sample/wordcount' + '../../../src/hurricane/tool/StartSupervisor.cpp' ], includePaths: [ '../../../include' @@ -28,5 +27,9 @@ module.exports = { }, linker: { ld: 'g++', + ldflags: [ '-ldl' ] + }, + dependencies: { + pthread: 'latest' } }; diff --git a/kake/Hurricane/wordcount/Kakefile b/kake/Hurricane/wordcount/Kakefile new file mode 100644 index 0000000..77f82ac --- /dev/null +++ b/kake/Hurricane/wordcount/Kakefile @@ -0,0 +1,31 @@ +'use strict'; + +module.exports = { + name: 'wordcount', + version: '0.0.1', + type: 'cpp', + target: 'dynamic_library', + targetPath: '../../../target', + compiler: { + cxx: 'g++', + cxxflags: ['-std=c++11'], + src: [ + '../../../src/hurricane/base', + '../../../src/hurricane/bolt', + '../../../src/hurricane/collector/OutputCollector.cpp', + '../../../src/hurricane/message', + '../../../src/hurricane/spout', + '../../../src/hurricane/task/TaskDeclarer.cpp', + '../../../src/hurricane/topology/Topology.cpp', + '../../../src/hurricane/util/Configuration.cpp', + '../../../src/hurricane/util/StringUtil.cpp', + ], + includePaths: [ + '../../../include' + ] + }, + linker: { + ld: 'g++', + ldflags: [ '-ldl' ] + } +}; diff --git a/msvc/12/Hurricane/hurricane.sln b/msvc/12/Hurricane/hurricane.sln deleted file mode 100755 index 8d1c597..0000000 --- a/msvc/12/Hurricane/hurricane.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.21005.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nimbus", "nimbus.vcxproj", "{4EAF81BF-7BC0-4753-9CAC-09270178AD9E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "supervisor", "..\supervisor\supervisor.vcxproj", "{10138BD7-AC7E-44F5-8D1B-B4E21BF32EA8}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4EAF81BF-7BC0-4753-9CAC-09270178AD9E}.Debug|Win32.ActiveCfg = Debug|Win32 - {4EAF81BF-7BC0-4753-9CAC-09270178AD9E}.Debug|Win32.Build.0 = Debug|Win32 - {4EAF81BF-7BC0-4753-9CAC-09270178AD9E}.Release|Win32.ActiveCfg = Release|Win32 - {4EAF81BF-7BC0-4753-9CAC-09270178AD9E}.Release|Win32.Build.0 = Release|Win32 - {10138BD7-AC7E-44F5-8D1B-B4E21BF32EA8}.Debug|Win32.ActiveCfg = Debug|Win32 - {10138BD7-AC7E-44F5-8D1B-B4E21BF32EA8}.Debug|Win32.Build.0 = Debug|Win32 - {10138BD7-AC7E-44F5-8D1B-B4E21BF32EA8}.Release|Win32.ActiveCfg = Release|Win32 - {10138BD7-AC7E-44F5-8D1B-B4E21BF32EA8}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/msvc/12/Hurricane/hurricane.suo b/msvc/12/Hurricane/hurricane.suo deleted file mode 100755 index 19aff86557d1e45320caf0f0c7cb5b7ea97c4533..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 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 Zr@f!u*5A5xquzfEv;VEj+eaba{{T^~nM42p diff --git a/msvc/12/Hurricane/hurricane.v12.suo b/msvc/12/Hurricane/hurricane.v12.suo deleted file mode 100755 index af09a3a3fa093621dbe002b8869e939c381f3a6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92160 zcmeHQ349bq)}H`!C~|`w9#Od=fgF$oMNCMz=W>gu{4tA77~S5>-arl-56I}>rgRDLh7rl+Uht5;R8UcGu% zy?a;7eYZZ?aep8roe-!W`0e*5frggr^^hkRsYM_#0+|MY-+ur7cV4RxCX@E|EV!$;3mX4%L zfZG5H0386w0)_$F1Nc4D44Ll=JOz*e=nOa>kPYYuNCs>K91Iu$NCFH3uznf=4g(AY zWC2b9@VV4+#{pUcjsmbgj{!6XbOa0ru)Q<|9052NFa|Ida5CT&z<9ttfKvez022YH z1BL)50nPyA0?q`S4QK*52apGt49Eve1q=tUK28HJ0+a&SBW3}}>yf~TfHJ^L0PCb2 zxB^fKr~)hoj05ny*}(i=q*R;zfAQrHR`>tK^T8O>AJPB6s_XyN{a>|)m0I&W3ApAD8{~wKO4FK%_9SnR+a*4ea3T7{MF8fP0AC{WOM#ck{Bq#S0W0L^mB3fZ{3_t9WqvjA zwK9J_@C`D56Yv_DzZv)znZFHqoy^|>yk6$-0=`@3Hvr!&^Y;U9l=%mNACmb^z>moM zqrjU1kIBzlfS-{0r+~N0{C40SGXD(lvoilY@C!2k67b71{|fM5WqvpCt1|x@@ZV(q z4dA^p{}%AuGXE~{douq4@IIOU5cngR{{;9`ng1O4?=t@n;4fvKbyQPH>EA+(K$RH9 z$}xgeAgAoHa`ZY{L&X?zI2MJbz8>r^@~@sV0i$bt^(Y{?P~Yib*ne>li!p9fCOW^N zE%ULw(aHYGMyWZrI;GCi{!;bfbg%kuG%}A<;UZ;0u4RGghLp>JP3u#9@Am(25Avt& zMjMBTbwRigLagim2X6nb$v&^zDsf%^k39eT|Iq)P^dINqsm2US=nA|4n12S%FYN9K zy|xh-9+wy0>2pRGIdT5WITq)1Y|oMJG{0#EUH2FH(VaeLm64N|^0x$i&J~&5>2vlS zIkEgX_<9ULxBP8D|9bgIH~RgIS+yhmsO$gOsOZy+5IM2@oQHD`u3P@DpntP`q`ac#$}f9^{*5y4R{nJN{vS2{*FgJTZTj>OMNVGi{{|j+ zmwcpq`BNej$2V+$lso-dy7GSu_v)A5RsY1%@1y)Y$hxL?amOZkms|OhQ2$O6=IIglLqu6|Ia}Ge(=lW zR{tr6_gCC&_g}St%AU#Yp3rO0@)hVOOh|B8Ec%b#j^$RiNPWzy%G+X#Rz{l=*O_41K!^`92d8-L28pnoaoKcG$D zsrPvse=h_59oqC=#~(xgb4@^3{#Ss$uKkzh*!xBO(?`Lz0A2c5fxfQ(PuHmbQb}8v z{s*{UxBaEN>mRJ%iL&d`{}A`*}90jq(f1;=e0#+;DLe z_3u3Tdh35|tNQitTp}<2w+cLx_7anu{!5C%{HF?Z*BR7x=sS)6qW{;&vl0Nh?T_n; zs{PTgbOAnJ2w?tV;3YDDDKKp-UcU^Ob`JBjwP^1!e*c$>^~EO=Vxp9OwS=3fAQQRZI;-YN5c1>Pm|uLAFp`M&|b zF7tbV-<0{cf!~q&_kiD*`F+6qW&R`JuK=F_K9$!#2mZUv{{#3-nP*x4iTpQ!Zvp?Z zT>lyQ9{~RW{0R67!21dRMLvMO{~Hh$6|cFs{|x>rVuJkMT3H`O6u`YEvnsmgw65GTi4sLG??TZfOs6 z5Op_`TmR$yH%Z)T`=9e~YumEDDdghopwBUc$(?>*#~G*>`fq~%HiNnj{Upa4;QXKZ zlsbiSkdJjl?|{D2KlLW3j2e5-{CP`{I*h-uiqd#oilg+oW>|CWzsRPKPnP;6w_4L= zqp!2q=Rq430um7?)&2dWUl`C}Y0neu|MksI`+9MJ4lfEX8tF6qzh35N)fW51s|!Ge zkAA7*hRZH|?fpG1HnjTolYh|q3TK$>FDWkb|8npAQ~l;I%D1TBsklNF8jrBGVu-j9 zDmorZ#d9zT>5mYcT!ghz=@y_;W*gs~WDE*a*XgK;Bwz*(1S{ntSycu2mcL5I?+X#Y zM>#ZFy|&#O8!~ohtmw6HSicD^ZRH@V$`;LwHUCwRhbs3tlsg}b^vMSAkM)}P`%OPI zm^1XEeSbUuv&u$M^UfbE@>zf2&92*UQIj>88yF2)S0XI47?Kymw&*lsxSr&u|B@ZG zkFdY`q8v(buuN`gK5n>K-sDC<$=&}@P1{gdZp88ZBEWi0`pNF|zc_lRk^#E(&&R!- zt1!8jKV94y;XnO<_3$07R9*TP;oe*2^W5n7cO3mh{WGrh>?r7ui_@2X{}}i`CV;-a z|INwzFv@Sl{&G&QEC16$U$_3#99KWg`OlOn=;wm|UvNKD{TE*D-MwD7hbne*mKxNR zs*7_gzDNTM0pO=bA|_nmWBfM;pa=@A!Wi3UL2YrC!5K&uXBQZtc&!48m?OVXV&Al% zv-HJJC*A$re-2*MW5rYKj`jsHI40MO_+&504lIbZ{$)THr!CxCaH`bGit*&H*YEzU z-?yJtF8{P;*=rf1#f%m?;chHd| zyR51DxM}>ZJzdEwn~pU_=6|sd{+-%S=m5xhPbvOHgrT+d^!RAt)J;EpdfejOn{T;y z?KvFGYKeax`J3giZ%9Rfr5k!)aYeV*pUz!6zGUqi+fIy*e>eT_X8&{i832+4rPN5> zEq#m|=x1bdr_Yf!aw2`bmS#S~YX7^_cUt`x`u})5`Xm7PFsVe&eSxoTm0!EluX+E^ zBcM%RDihn6Qb)}TpXmA9inpdVPI&pcbtk78Rb-Vd^H%>0``^kK^h#o|EM;gZO#hJ|{i*biyZ_9|ZzIOA z+wLn_*r98W5of*8zst7sHrWowcK)vwzU6QF&+PQSsegn9or0lksxePDqry1~JaT1| zx#zcAxc-xoul1gGdxs)Bsf(R{LIW!gpC9?_=ubU6qA2jv&z)xuzjoxpJ3pPjZte6= zbe)H_8eZdQ{OImh14?%6Zn-Hda7(Q0A3eE?wzK%!X&W2e`01LeB{vOuJf%LDM#6ZH z<2&3-qij5t?No#CuPj*-fY!WavZ9Ctflbd zKUVVh!}o0H)lfOO)#vb#GLL%U%BnS|w#<39W6v*#m0tFsU73d&I>YL}PcW=8@8uW2 zC;v(gc6`@#KeE;(%Xz71{j0~nj{j5ip`JIc9MZ6G`}IplX7ubdcA(+lv~nAqZ!8<( za?kwhwVTlS14j{a{ei1VoY5&i?v7tNd^_m&^rbI$O#7wF69rskb?iNUWqTyS} zf9B?iD|)Q%z5l~!k9@6g=SzlwgJrI)!$~NWlE3%%5c~4GXa&+(ZZQbjfcc^rV|M@#NxUne8fE+ZW z={GG;pLNXaZ@zw^`A^q7Gvvd68=d&Fa4-Y?x0w(gz4UVpAN1BUSNwARRTB?h*XD>Z z?@gS*C=1Od)EKq3pZPocKzEC%ip>p z@KA%lFK%+(mp^84EhbDThE1n@z0UsM%GmI4FRp0Hpw{2)-FBjp!~Z+aJO9yEd#Krd z>W$wUk6p{5<@wBPV~>UmqfA2ITo6S>S& z{XF&DQeL!4KI|W-X`5*_sxYvzuz9%lE*2tGPu#Ne@@p>cHUF|m?6rSKF6w~><%{&m!M=<-2u8Hxfg zbbNEz#M!fN*xqZ<_O8b*pcNFZ^m1{L5eLly7gvcGlY?=%CV{1<~7qnXHqBVqaF)1bdi=5^P9sKhH^e&-_E zP2`5dWy%vHAL(|TTaqr)hPC~R_eHt4JQpZE2yA6fJIOHBed{%l_oxAs@J|Kot){}Fcys@CiN53!fa+HThLJfhpGAvbLO;_6Nf z1~lyMvIZCH`E$75v73AL*`M)R%I29bzxL{?N88rV7;WFyRljWj{nJK%@Zmqyu^;XH zpqg_}b>?D)V%rw`SwCa}l(z6{hqo&+`Yb><8}y8vM_f z=KV6@%i()kUU%E|cmH!Y*;2!d!k9KOcGbQe_?5q#{&%vy$t?EY2QkcRZmA(|(6xV^ zY#-9`Lca-qr}iH!YesF5In`LLF0Zz7?4?f1B6*>IKJ<@T|F>?a8U@R$6u?MpMz@bO zmZj+{EI{nN*qX(4b8G4k`;QHUJEw@P-$lwV2K}b;OSk$@*YN)o;aSrFy7aFBeSQ1i zb^k>i7YEOag8p@&uj_xx&?rC4trFO9O*ckCKV4)0%?wby6hIzaiQ|T~ps)O|ZuQ^a z-TxMK|341&H%Ug_={wCn#r$ugEW2*`PXK*g|66~@*`HW{Iz3AInV_%h|LE`NeGuhm zx$`6sQml?B8}wEE7X|LlKV{er1^e1O|76P4xreVB!=Lhr))e`1E8?QtfH?{}Eg!n9 z40@~#^T*0SJl>bU9X(ahd?olqhaq?LbJUAR>*Hq2zLS8}{(PESX=1eh$2ojV8C_dm{^F?t4Q}6c-6NB)JbdMzL0UfhJ*{~fFK7do)5aVhkB1Yc2=*!RA)UW*HV+n(_CfJ3fLN*ioj ziQE^Rg$IbEGA#SQV?Tci<~p<-e&W~lCO|6wFVaG8QJ_^yph2^)9kwsKZ~G6KbEZ9G zD?yvK&r3f1=h3l00qTk2%QVsGWGM>dRKC2k<0+%AIqZz5t8$*NIyM@%t4rgj{pu9J zO%aJO!mQny&+h8hxZ$Lqnom6In8`=k6-Ax866~$=?!PnRS8FETN;j?O^~gx5g*=RRDhB$~nMd}!Y3q=+V+xlK&s;vtHvH5)6UY1fPsrcR{z`MV ze~kGDXC<^4*mB&G)&J{8Kh>RnkhS7Qo8M1?{d23l(T#qZhW%ZDXDMq*KGqS@_x-rM z!;OAAY#*n@_3wP#Fdd*Pe|i~|{mXeE#Nj!8Gnlh!@5%f46RiVAjYHvze0pzqKV2x_sBa@4vCVU~?ZVf1P@9$vo&&TK}}LDjoSy>)(uH)}@~h`c3HsMGB?QGoKjoJQH-C{J!GX zqW>{IfO=e){$$Ws{>NDFzY7gSanfCH{p;(#XC-~Rx$BdQ{*~HsyzNX%oIYr_jGJm; z{Z+?~)+tBmIxMDn*(-B9-`^wqshxe|zIbfkTdXzJQxxWrQaofzl+5Z6R=#L8bK;3h+Dwt|vtG}`hjsFTdjA{sJkR)L-P8UPVY`eea!y{>KWQ(1%0ugr zNS9Q~p?T^5HxHgvoQd;7lwJ%S{|9)`e^&jUjS@#1f5+mx3XD%mH{10Ey#F$;6UuQo zKv(`;(|)lT{vkQh*BSqz|Ks>5#un?+4=#VO{Z09E4CJ`P6PIa=Iwk5q(mgZ^`oG}b z4`uy_+P^0Kxkj)!M?t6LW&Cw2|0tAqhS3Y=h8%Wk&qZ%pe$FR!<^MhI9WNgjT7LWa zht(c5m=l0l!;{AXnp^#8}+|75%u;fg5AU&HTzivIjF zjQ=YBArAR^fcgN&#We)5e>4I<1keO1CfT@5Y0IyF2o(?Dh%m9=E$^bI~=K{(B6@W@W6<`)%Hee25 zE?^#DK41Z0Az%@J&n4K;KkQEV3hMUNAE5sXy@N*?srn0t4DF>K>x&!4!22@|vmwsE zLdW$~4 ze(k?^@G)OImBM;dzg+|H=J$Mm;M+&PUGeG($!YDU9e!BSD|Q?t7V+xUp6lOSKhyEw zI=2J0=AqW$_0J*mbADw_uKVqNYRH@CxB0IA88k_} z`eNksREq*1tk`{R*H0cl`~ANxI&oXapDwn0YBX^i+=3tc9-&z8pUTHvF8G|-nmk9g zvPSlQ>9pV2#F4oBpNy@1to8_r{wLZ~-2hbk5pD~*T!2q%{n5?-aE|^J{=Z}Kykh{m z^cgR{9>C;A-)Zj4-?RLrdl2%v^cgF!OP~8sof7>ohe+DG^cgR%=${6SP=Zw->)|Ty zs}3edEdOA9>?nCKzlogtH@@P0k!d{qO*28CnwP$|3d0-75v|Zz8=VFJL*GCzakI_; zo}k|c*INU0%Wt*6-ReKtef>Xi|q{^i$mH-zYcB&Lp1d{Qo6HWQ55oj8hvjY?q^~*q$`$vq5&}V81wWea$d_k z*LJC6m{&l5GuM5%??_#v50rVXM{qvg9>BN^>IUT_XRHS2%0~g>0lc5On>w8NF2G#@ z98bCfdH{L?r~!Hd5&($+_IYYX?ha!6q@L#Qxn9H-JN8M&MQ~ka0D%3I@$~}%CjbTk zP6T8EvH;X!*?=6t5WrBtFaTqVMgUF%j0B7Vj0TJWj0G_E=@h^?0DZcr0ww?^0!{;* z4wwYE8ITL$S|Ry78~7ZVpA4KY^M$}uWS$0cvCK~grYD@gEd?%<`E!BGWxi74S-`Vp zelGAlnO^|BQ0C7AUJP*S|0x>#Pbfpm4y^qSL+pdSBS&IW7 zRES~UGjBWVzU{}KKIzge=S+O4C2QUwP4X&*Z`W174mo!##_!*=FGo>eeYJ8$E>qyOH9w|u-T<+{;NeY<%Ra#gIp^9hTt@u1bmx~JF}3>!nGWXfBZBAze~oaOnko(6_ z6<^^f;+_}C)lA-_sPVTPpBCVGrVncxenWqfdP4`hkh$L=^c^HlK8#S`QSjkeV!wtM z`ImU+AUZFQsScD<8;(ApPO%Xq{iU9HF+Cx6p0sEqxy77zc@1#mzQ4xpy%wsjXgRI+ z!I6%qBed1nMU0wjG~(nET1p%RZ}7~U*^Z)V(K@^-+0jb8s0R4ckzuggK6u-juum&J zbK=&@g|Wa5kE)NUpxrnrM=GnDrp4&JruFOsb6^zb z%Bu4#7$2p)*kabUEJkv0_NWK1tSHH}tp&-LZC}Oj279-}-apP={j?ChUP#|_CFXs> zn}5aZ)%%#I*FxMq#zGRb-~EhvI+8G3tI*tb@dbh#&iz;TXt6rnIJex;wIJcgxXK4l zP9usJ`(E`X&U+X6YlUjP`%PJYs+rIn{mJ#1pr`|~`)-^8htO0cAQfel}XmLjD(9y{Wc8#js5Z zF*o9zmiraIS zkt&y6@3HIC!Fo^GXN+r!^}HvtXPRo4%>Ldpp%S=gvkvip`b*Op(qE?Q)(y7x&L$#^BerIhk25MG}q+qs6`?eSl+J0} z{fsET$d6?F7vu71D>3*e@}1^49YKGM{OC^KIex=S{MQknzh0X@^=0J5^0RGB2I!VQ z9`vt~kK~)#&3>WAP7!3I+TKC9H&!EuNn6aPMbG3Krt-0v8oWOE@H3L@=m^tyX#c`> zangc!jb)Bs?z0#D*|Lab^;1VWvI(_7SywZRxF?<%A*`*IsH0d%h(w;tI_+f=F~ELg z=Y6n0xd(*Foxbx9 z2ru^c1EBweAn!__``@_FC{n`yx2Weo4}<!t_U8nde`%2ZJHZvBVXxSbC> z)Gui7!yOM!{CKnV+g!M98M@{zcF&Hl0dF?O1&tfcXLM=P&8W&z)~EHcZmkNNcwSUK8)XajJi} z(S-J&)9-ccJ|O{}_uyIIZ6o9#>-uw_X$$VR?7aM%i+jz#Y}n-`pUr=JHK#*iZ7+Bi zgW&&?p%+f-(|yFgU%I^aK(l(wE(pFS7v6DeltwOMGlG5QlDg*?L59*N-)`w?%>TB@ zn?mDBivnw3dG)ga1N-fqwZ83lJ4d|o2yYCveCMLo2oIC@^ZWGrpS^!A(t7f_mwmqY z;jPbZdVS+T@7y^gC)``D>cf1yyE{%cx68$(zfA2@f=B7P*aM}-} z;%?dSW#g8gkk8P5BY3C3;&G9U`S?Zs&0UC$t79W%td?~~!86{iS~+}Oz)l)wb5=98 z;b@`eKKfoLou6ohP>alt2Xm%o&PQnf(p#r*|Av>}X*AT+=7B&f@Ze{UiB9?5M`raN z8*@MKFz^-Yl}B^L6&{65tPK|!eVoyXVr8T$`0zfDcD7hTEgt(AEHUo#{62rtq)x3R z+8RNKDMFsG3<=KC8H* ztlW}KoRvRkW_j7P1=#~~#{w7TS61X^=T+wAvNB zq8`H-ww}Ln^tSoqhTe24?C(`!bj0Iik#Ku(MH{Gz1ZFEB0DHw7*qhYpp4X>>XG5p zsJA&5&4QG;=clvNt;)pc?R-;k)J;#tr3P`QTb0%Hu&7z1h1SGOM{h5!U9(Aq)bfpY zH1W-F(+rMm+ZRJu*~W))-O~EU{ww~RbLuXI@hqf~fzR4>*|YK3V~76bll=?3O`btl zIMTR4=R~;C}VGq82&#u;YOl&?f(Co#ICI4-5?3k6mU-ILw9k+A&&f)jzQ`+qK zZO4%#Z(mbz|FuW{Gb6g+^Vy>?KjJwX#TFZvdz951r0B0@>>wk+$o72W)GVR-7_a8% z_kJ~JB0A6P5WdCvyH>87gGMG67~``r z_KUZ_W}!wo1vG1JU`WmREnOl;FZJ##>*;82e25k#*5zyBq&B@4zzvW!c|M%~ny5($ zc&iDj(glM*tr`u9ypN$KA&BRP3+a%OV! z!Y89H!%XPO{9Q}0eDBwctC~MK>8phsN1Oq1gn2Gk;2Nr^DB5FV=nj}jQ`X%HL-tKhktV&!6~-0 zDDcRYP3E58ZsGb*M!wd2+U*@U@d_)4)nBtX&g%g-=6u9yPUJp^RTjH9|9`0eI@BBG z>~RU+i7DpK_wTuP`B$>r@(23=X^&gu zW@m1mxT44E-upj%_Q=-?cfQovt_7O%`(xKH>+0XH|69fBM&9?RH)pZ)vI$Mcx4W9B zbaqE`iuFF&VBZO~WuCEqB4+b#z=0p%tdq73p5N-7iln@Lb?5)&Vr7Cm&%$E^l#EVB z-5q%6|HL{jE_|gUeEpM#0wbkkz2CvKV}kbzl70h#cmJrfS6{hK5!LQ~v+y{r@tC_< z_vwi-fuTkbtTqr)KoU1*wgH1Kb2m~QG4|DPG0fTuk`q_dWbn9m>zXe zS3Hj()lm3wyvzJWWncOQxfR_KW!x19u(X@l&0wd zF8=saXAZoRcw#j=>sTnNZg`%-A9emqP3U+X{b7!X&fXsJz9>KTqf>ve_SA94s7Jnn z?Itg4#u0s(m1Simx#KGH$}3GTW;E{0JCyq}H79dOI@Y!m(y|8kO-N1amy(b%ICF49 z-;Ct6zDa56nb{dR3m23Y&zM|Q;j+MOVYaw5zoe?5(BgERoL5npJ0Y*UIIpxau}Ig; zx<#>hpm>U-k?}LZ%v<+M^DLfN#a5WskG;l@2W{`xvR>3$sFiGQeQ^}y=}a}T7udOX z23tDz+$gO%3LM2kd&hp6BfQr|Uh2^9f}4X|@gGr&%Djr{x#RI~cy zoV2mvFk!a7nEWBAP*80 z=l^)O48rXccU=_j!wQaf=czmHagveOcbr~H*0eP=M_Fun%lV1NFDce~cCYn{Fj%^1Tz!IKvJj4z=) zxz~lcZ&i#qjFB}xdqTf6PS(##Unp00q4eRavBM9j{eOPqJM_vQyo#uHLvwyqh8Wbj zh+!y$#aLk2C8A!2`K%Y!W9{n0x#gNGirkmzU2~cir2T&(8~J1yx$^c^>tbpe!^bMPJ`~U3sA7WtewSTW}|6k-?-F5r_ zim)RwbRS~f{y(u3vu^*N&xP3oV*j5wFG7F$8eY;kdP=XL1WE=|aa|^bhpFo^ktK@SNON84&{U#yK5T@cb6T100W&l7jb~JH0LSoo9Eh zy6VltO3(S`i!Z+UN5@1;+l_zUXA@K(H=uuJlx3LFQp{SQ;n zG?nY@|LTcAo&C>Ku>QpMzvV30h<1HsgDr})$=aGe1ZD2w*CTzq5-bm^Zj~}4JPJJU zMWhznjz#8hK&4$laTzM_fv&8VT_+7GI^V$k6+r{l77bXl+VSwW6ZY zt_@O0nAML{l2cg7^i1^;xK!q|J?&|`+VlBu!hZyphMn8UdAsKHz<=L(Ze!auD~F!> zdGov0#j^x%jtW^yBEBneEv}gD$JF%$s6Mi4{f!!7*9o`T~6U!D{xEa5l8IdH&$u!N5RSZZRBrwS2ZEyHlC=fejEIac#7^d zK7(fq56w4@SD#v*HzR&XaYeq3c$L2+4p zQgUM7+zEqoRUOVLFPt*FJkM~7H$1+ys-)xu!N_o*51vw0nqOI5R+`7%OM#$A=lFc| zEQFwym4`i6@U8jZPR{x8q?W?q*70yFGTa-w7*N~ZT<_#oT&;`hW(2hJfGj!$!b|0Ga>} zme-mBA1d?Bfm_IYE8x~L-xj!?%(n;bAoHDo5108Pfsc~;qk)f+`Od&yWWF14cbV@A z+)L&YfD>iDFL08~rvRtQe1G6Hna=qz$eOl7Vu!1&jB7H^TU9L%lt{e zBV~Rx@EDmt8Tb^L9}j%0%ufV9P39*7pCR*S0-q)G=K$x)JY`;hd?BD1FcnY);Pq+1 z(*Y%b8Guqi8DJ*hTtGRX0#FI40?Y!;2FwA>1)J=N7laNC7&x#sgM22lYNQ2 zpC4?t8hZm$oYLohUSHAlJMR8q-h;!pw*6P1tJY@hq7>0EgHmM4ic+zSEC+Hyf6?u55s~_j#czG#w4?#uZ#d+Q zy*t`oy3(%P;_W`_+!cb&Sg7$0r@LzRJl%t$S(xY3+^U`FMKj>f|<8i$@n zEzDMBJCb&2;xz)xbrHagFCX)t;070|8biC`aLY!m;q|*rg`Fk#C-nojOvK3mo{6YR zs7i%Pu8H?XZ^uV|tm1)pFdu9&)?{5o-XpIgYIDa%q@BiWr+2a#NW`2H)k!%TYd;tE z02?&t22N7uX-Q1P^jG~kq)d*ZP1fEK?QePiv8PXLk^Ac>vz}SAd+d+A5H?Kp<<*F( zHu}u1k~%THF@KKQ{Bvj~>$~7AuBG6(o;v@($G7WQB&r-eH4}zW=l{1^(XMv7>&Bba z`TqkB=D&Dtu-(BIT7L70Lq?td-y)hi|GyFjEt%-&4Uz31>o?5X&l^ZR_#0l)zs4um z`Tv8K6%6_5{v z{C_h8b^hO2oE}!^|E+|d$n+9LYQ_KG{l9hoAJfZCTSBczD7(T5>sgrAyVTQoji9?q g5UKU*L`dlROzZsrwISs?|9@>rxz7KuG>senKSG*8=l}o! diff --git a/msvc/12/Hurricane/nimbus.vcxproj b/msvc/12/Hurricane/nimbus.vcxproj deleted file mode 100755 index d69fd61..0000000 --- a/msvc/12/Hurricane/nimbus.vcxproj +++ /dev/null @@ -1,130 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {4EAF81BF-7BC0-4753-9CAC-09270178AD9E} - Win32Proj - nimbus - - - - Application - true - v120 - Unicode - - - Application - false - v120 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)..\..\..\include;%(AdditionalIncludeDirectories) - - - Console - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)..\..\..\include;%(AdditionalIncludeDirectories) - - - Console - true - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/msvc/12/Hurricane/nimbus.vcxproj.filters b/msvc/12/Hurricane/nimbus.vcxproj.filters deleted file mode 100755 index 87d0aad..0000000 --- a/msvc/12/Hurricane/nimbus.vcxproj.filters +++ /dev/null @@ -1,210 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - {ffe24b97-de3b-47ca-ab42-2f816437398e} - - - {199d07a0-bb3d-4b59-8e61-5de4b9d41fbb} - - - {09005c13-5d7c-484b-b00d-a501b5f2c1b0} - - - {1151322a-3d97-4ecb-96d3-c44b078d74cb} - - - {fb611766-ef59-4ff8-ae64-4ea489cfaeac} - - - {98820893-8530-45fa-b3bd-91d692c01c2f} - - - {68c7eba0-de2e-49bb-a6d2-9a14028b8b09} - - - {ee45ab63-07e8-4c35-b620-d5952a27925b} - - - {0299d7fa-d962-420a-ab54-f134589d4ca8} - - - {3428b634-33f6-47d4-9e98-96b7f42f1cdc} - - - {52b08f0d-35e5-48a0-9785-b9cd64c569b9} - - - {b3b88421-40c6-49d2-aaf8-6115ad1959d4} - - - {59cd36ce-74ab-4358-92cd-f7100273953e} - - - {9260776c-b3a2-4f82-b411-9e44fbf4b4b0} - - - {dbcdca99-c194-491a-a292-f8324313b6b4} - - - {9fc23f85-64cb-45c9-b42e-834064979719} - - - {55fe557c-155f-4e7c-978e-d1eb2ce2f57b} - - - {5f204b6a-131d-471e-9708-bdb32cd28adc} - - - {349da14a-0a4c-4882-92a8-10d77c8106b5} - - - {544a6286-18be-481a-8977-218e859a691d} - - - - - 源文件\hurricane\base - - - 源文件\hurricane\util - - - 源文件\hurricane\util - - - 源文件\hurricane\util - - - 源文件\hurricane\util - - - 源文件\hurricane\message - - - 源文件\hurricane\message - - - 源文件\hurricane\message - - - 源文件\hurricane\service - - - 源文件\hurricane\service - - - 源文件\hurricane\tool - - - 源文件\hurricane\util - - - 源文件\hurricane\service - - - 源文件\sample\wordcount - - - 源文件\sample\wordcount - - - 源文件\sample\wordcount - - - 源文件\sample\wordcount - - - 源文件\hurricane\collector - - - 源文件\hurricane\bolt - - - 源文件\hurricane\task - - - 源文件\hurricane\spout - - - 源文件\hurricane\topology - - - 源文件\hurricane\topology - - - - - 头文件\hurricane\base - - - 头文件\hurricane\base - - - 头文件\hurricane\base - - - 头文件\hurricane\base - - - 头文件\hurricane\base - - - 头文件\hurricane\message - - - 头文件\hurricane\util - - - 头文件\hurricane\util - - - 头文件\hurricane\util - - - 头文件\hurricane\util - - - 头文件\hurricane\message - - - 头文件\hurricane\message - - - 头文件\hurricane\message - - - 头文件\hurricane\service - - - 头文件\hurricane\service - - - 头文件\hurricane\util - - - 头文件\sample\wordcount - - - 头文件\sample\wordcount - - - 头文件\sample\wordcount - - - 头文件\sample\wordcount - - - \ No newline at end of file diff --git a/msvc/12/Hurricane/nimbus.vcxproj.user b/msvc/12/Hurricane/nimbus.vcxproj.user deleted file mode 100755 index aad40cd..0000000 --- a/msvc/12/Hurricane/nimbus.vcxproj.user +++ /dev/null @@ -1,11 +0,0 @@ - - - - supervisor - WindowsLocalDebugger - - - supervisor - WindowsLocalDebugger - - \ No newline at end of file diff --git a/msvc/12/supervisor/supervisor.vcxproj b/msvc/12/supervisor/supervisor.vcxproj deleted file mode 100755 index 60ae777..0000000 --- a/msvc/12/supervisor/supervisor.vcxproj +++ /dev/null @@ -1,140 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {10138BD7-AC7E-44F5-8D1B-B4E21BF32EA8} - Win32Proj - supervisor - - - - Application - true - v120 - Unicode - - - Application - false - v120 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - - - Console - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)..\..\..\include;%(AdditionalIncludeDirectories) - - - Console - true - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/msvc/12/supervisor/supervisor.vcxproj.filters b/msvc/12/supervisor/supervisor.vcxproj.filters deleted file mode 100755 index 9bca714..0000000 --- a/msvc/12/supervisor/supervisor.vcxproj.filters +++ /dev/null @@ -1,258 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - {6db5b0e3-3360-4c96-b4ac-37b65711179d} - - - {6381391f-5970-40e1-802c-28062f38206f} - - - {bda14243-586f-4349-a8d0-c24ec53e841b} - - - {33dc06f7-95eb-47fd-a525-4810ef2ae260} - - - {210d0cb1-906c-4275-bca9-afa3ccfcebd3} - - - {f31bd5db-5faf-4016-b6cf-481571d76e6a} - - - {8586cbcb-14c3-492c-8a5e-d0e4713f7e4b} - - - {dc02e35f-b93e-4e58-b786-011eb32ff7ca} - - - {1d22a637-81e2-49fa-be38-ec59b107b8e1} - - - {54efb49c-98ff-47f5-90bb-55806423e6e3} - - - {ef039453-6bf0-4a40-a6e7-3c6881ddfbaf} - - - {fd69eae3-be8a-49c1-ad1c-e7d872c9057b} - - - {1fd7e809-e787-4bab-8352-31d76b35a5bc} - - - {18984799-86aa-4c61-ab60-64cfcce17f62} - - - {b5dd368e-0c4c-489a-8928-859dd4247b21} - - - {74d0bf59-849b-432c-bf43-7c7617c50e09} - - - {23be807c-8b76-4592-a8c1-56fd40a607fe} - - - {a1837d7e-79cb-44b2-834c-7ddb8707f849} - - - {aca90d15-7604-45af-8f6c-c2d69851571c} - - - {2d61bf55-be95-4909-a94b-5cffdba7fd79} - - - {3c2b553f-5a75-486f-87f9-8ab4de852cee} - - - {33394e40-b3a9-43ec-90b4-b0120b428f27} - - - {1d179d8d-5d71-4c4a-ba64-b48561089f30} - - - {5e801fe5-9744-4f5d-8e68-4ed6f6437568} - - - {c71a2b18-e63a-4b63-91ab-749a33415d95} - - - - - 头文件\hurricane\base - - - 头文件\hurricane\base - - - 头文件\hurricane\base - - - 头文件\hurricane\base - - - 头文件\hurricane\base - - - 头文件\hurricane\message - - - 头文件\hurricane\message - - - 头文件\hurricane\message - - - 头文件\hurricane\message - - - 头文件\hurricane\util - - - 头文件\hurricane\util - - - 头文件\hurricane\util - - - 头文件\hurricane\util - - - 头文件\hurricane\service - - - 头文件\hurricane\service - - - 头文件\hurricane\util - - - 头文件\hurricane\service - - - 头文件\hurricane\task - - - 头文件\hurricane\bolt - - - 头文件\hurricane\spout - - - 头文件\hurricane\task - - - 头文件\hurricane\spout - - - 头文件\hurricane\bolt - - - 头文件\hurricane\collector - - - 头文件\sample\wordcount - - - 头文件\sample\wordcount - - - 头文件\sample\wordcount - - - 头文件\hurricane\topology - - - 头文件\sample\wordcount - - - 头文件\hurricane\topology - - - 头文件\hurricane\task - - - - - 源文件\hurricane\base - - - 源文件\hurricane\message - - - 源文件\hurricane\message - - - 源文件\hurricane\message - - - 源文件\hurricane\util - - - 源文件\hurricane\util - - - 源文件\hurricane\util - - - 源文件\hurricane\util - - - 源文件\hurricane\service - - - 源文件\hurricane\service - - - 源文件\hurricane\tool - - - 源文件\hurricane\util - - - 源文件\hurricane\service - - - 源文件\hurricane\task - - - 源文件\hurricane\spout - - - 源文件\hurricane\bolt - - - 源文件\sample\wordcount - - - 源文件\sample\wordcount - - - 源文件\sample\wordcount - - - 源文件\hurricane\collector - - - 源文件\hurricane\topology - - - 源文件\sample\wordcount - - - 源文件\hurricane\topology - - - \ No newline at end of file diff --git a/src/hurricane/base/DataPackage.cpp b/src/hurricane/base/DataPackage.cpp index 0456161..e902ac2 100755 --- a/src/hurricane/base/DataPackage.cpp +++ b/src/hurricane/base/DataPackage.cpp @@ -24,8 +24,13 @@ namespace hurricane { class AllWritables { public: AllWritables() { - _writables.insert({ 0, std::shared_ptr(new IntWritable) }); - _writables.insert({ 3, std::shared_ptr(new StringWritable) }); + _writables.insert({ 0, std::shared_ptr(new Int32Writable) }); + _writables.insert({ 1, std::shared_ptr(new Int64Writable) }); + _writables.insert({ 2, std::shared_ptr(new UInt32Writable) }); + _writables.insert({ 3, std::shared_ptr(new UInt64Writable) }); + _writables.insert({ 4, std::shared_ptr(new BooleanWritable) }); + _writables.insert({ 5, std::shared_ptr(new FloatWritable) }); + _writables.insert({ 6, std::shared_ptr(new StringWritable) }); } std::map> _writables; @@ -39,13 +44,21 @@ namespace hurricane { } std::map Variant::TypeCodes = { - { Variant::Type::Integer, 0 }, - { Variant::Type::String, 3 } + { Variant::Type::Int32, 0 }, + { Variant::Type::Int64, 1 }, + { Variant::Type::UInt32, 2 }, + { Variant::Type::UInt64, 3 }, + { Variant::Type::Boolean, 4 }, + { Variant::Type::Float, 5 }, + { Variant::Type::String, 6 } }; std::map < Variant::Type, std::string > Variant::TypeNames = { { Variant::Type::Invalid, "Invalid" }, - { Variant::Type::Integer, "Integer" }, + { Variant::Type::Int32, "Int32" }, + { Variant::Type::Int64, "Int64" }, + { Variant::Type::UInt32, "UInt32" }, + { Variant::Type::UInt64, "UInt64" }, { Variant::Type::Boolean, "Boolean" }, { Variant::Type::Float, "Float" }, { Variant::Type::String, "String" } diff --git a/src/hurricane/base/Library.cpp b/src/hurricane/base/Library.cpp new file mode 100644 index 0000000..bd82ea9 --- /dev/null +++ b/src/hurricane/base/Library.cpp @@ -0,0 +1,43 @@ +#include "hurricane/base/Library.h" + +#ifndef WIN32 +#include +#else +#include + +extern HMODULE LibrarayHandle; +#endif + +#include +#include + +using namespace std; + +std::string GetLibraryPath() { +#ifndef WIN32 + Dl_info dllInfo; + + int ret = dladdr((void*)(GetLibraryPath), &dllInfo); + if ( !ret ) { + std::cout << "[ERROR] Get Library Path failed" << std::endl; + exit(EXIT_FAILURE); + } + + std::string libraryFilePath = dllInfo.dli_fname; + std::string::size_type pos = libraryFilePath.rfind('/'); +#else + const int MAX_FILE_NAME = 1000; + + char moduleFileName[MAX_FILE_NAME]; + GetModuleFileName(LibrarayHandle, moduleFileName, MAX_FILE_NAME); + + std::string libraryFilePath = moduleFileName; + std::string::size_type pos = libraryFilePath.rfind('\\'); +#endif + + if ( pos == std::string::npos ) { + return "."; + } + + return libraryFilePath.substr(0, pos); +} diff --git a/src/hurricane/base/Values.cpp b/src/hurricane/base/Values.cpp new file mode 100644 index 0000000..040434a --- /dev/null +++ b/src/hurricane/base/Values.cpp @@ -0,0 +1,21 @@ +#include "hurricane/base/Values.h" + +namespace hurricane { +namespace base { + +void Tuple::Serialize(Variants& variants) const +{ + base::Variant::Serialize(variants, _sourceTask); + base::Variant::Serialize(variants, _destTask); + base::Variant::Serialize(variants, _values); +} + +void Tuple::Deserialize(Variants::const_iterator& it) +{ + base::Variant::Deserialize(it, _sourceTask); + base::Variant::Deserialize(it, _destTask); + base::Variant::Deserialize(it, _values); +} + +} +} diff --git a/src/hurricane/bolt/BoltDeclarer.cpp b/src/hurricane/bolt/BoltDeclarer.cpp index e8bf300..914ebd2 100755 --- a/src/hurricane/bolt/BoltDeclarer.cpp +++ b/src/hurricane/bolt/BoltDeclarer.cpp @@ -7,6 +7,12 @@ namespace hurricane { _bolt(bolt){ SetType(hurricane::task::TaskDeclarer::Type::Bolt); SetTaskName(boltName); + + _fields = _bolt->DeclareFields(); + int fieldIndex = 0; + for ( const std::string& field : _fields ) { + _fieldsMap.insert({field, fieldIndex}); + } } } -} \ No newline at end of file +} diff --git a/src/hurricane/collector/OutputCollector.cpp b/src/hurricane/collector/OutputCollector.cpp index 655785c..91d7c85 100755 --- a/src/hurricane/collector/OutputCollector.cpp +++ b/src/hurricane/collector/OutputCollector.cpp @@ -1,13 +1,15 @@ #include "hurricane/collector/OutputCollector.h" +#include "hurricane/collector/OutputQueue.h" #include "hurricane/base/Values.h" #include namespace hurricane { namespace collector { - void OutputCollector::Emit(const hurricane::base::Tuple& tuple) { - std::cout << "Output Collector" << std::endl; - std::cout << tuple.GetSize() << std::endl; + void OutputCollector::Emit(const hurricane::base::Tuple& tuple) { + if ( _taskIndex != -1 ) { + _queue->Push(new OutputItem(_taskIndex, tuple, _taskName)); + } } } -} \ No newline at end of file +} diff --git a/src/hurricane/collector/OutputDispatcher.cpp b/src/hurricane/collector/OutputDispatcher.cpp new file mode 100644 index 0000000..b51e94c --- /dev/null +++ b/src/hurricane/collector/OutputDispatcher.cpp @@ -0,0 +1,171 @@ +#include "hurricane/collector/OutputDispatcher.h" +#include "hurricane/collector/OutputQueue.h" +#include "hurricane/collector/TaskQueue.h" +#include "hurricane/util/StringUtil.h" +#include "hurricane/message/CommandClient.h" +#include "hurricane/message/Command.h" +#include "hurricane/util/NetConnector.h" + +namespace hurricane { +namespace collector { + +void OutputDispatcher::SetTaskInfos(const std::vector& taskInfos) +{ + _taskInfos = taskInfos; +} + +void OutputDispatcher::SetNimbusClient(message::CommandClient* nimbusClient) +{ + _nimbusClient.reset(nimbusClient); +} + +void OutputDispatcher::Start() +{ + _thread = std::thread(&OutputDispatcher::MainThread, this); +} + +void OutputDispatcher::MainThread() +{ + while ( true ) { + OutputItem* outputItem = nullptr; + if ( _queue->Pop(outputItem) ) { + int taskIndex = outputItem->GetTaskIndex(); + task::TaskInfo taskInfo = _taskInfos[taskIndex]; + for ( const task::PathInfo& pathInfo : taskInfo.GetPaths() ) { + ProcessPath(taskInfo, pathInfo, outputItem); + } + + delete outputItem; + outputItem = nullptr; + } + } +} + +bool OutputDispatcher::ProcessPath(const task::TaskInfo& taskInfo, const task::PathInfo& path, + OutputItem* outputItem) +{ + std::string sourceTaskName = taskInfo.GetTaskName(); + std::string destTaskName = path.GetTaskName(); + + outputItem->GetTuple().SetSourceTask(sourceTaskName); + outputItem->GetTuple().SetDestTask(destTaskName); + + if ( path.GetGroupMethod() == task::PathInfo::GroupMethod::Global ) { + const task::ExecutorPosition& executorPosition = path.GetDestinationExecutors()[0]; + + SendTupleTo(outputItem, executorPosition); + } + else if ( path.GetGroupMethod() == task::PathInfo::GroupMethod::Random ) { + int destCount = path.GetDestinationExecutors().size(); + int destIndex = rand() % destCount; + + const task::ExecutorPosition& executorPosition = path.GetDestinationExecutors()[destIndex]; + + SendTupleTo(outputItem, executorPosition); + } + else if ( path.GetGroupMethod() == task::PathInfo::GroupMethod::Field ) { + TaskPathName taskPathName = { sourceTaskName, destTaskName }; + + auto taskPairIter = _fieldsDestinations.find(taskPathName); + if ( taskPairIter == _fieldsDestinations.end() ) { + _fieldsDestinations.insert({ taskPathName, std::map() }); + taskPairIter = _fieldsDestinations.find(taskPathName); + } + + std::map& destinations = taskPairIter->second; + int fieldIndex = this->_taskFieldsMap[sourceTaskName]->at(path.GetFieldName()); + std::string fieldValue = outputItem->GetTuple()[fieldIndex].GetStringValue(); + auto fieldDestIter = destinations.find(fieldValue); + + if ( fieldDestIter == destinations.end() ) { + AskField(taskPathName, fieldValue, + [taskPathName, outputItem, fieldValue, this](task::ExecutorPosition executorPosition) -> void { + _fieldsDestinations[taskPathName].insert({fieldValue, executorPosition}); + SendTupleTo(outputItem, executorPosition); + }); + } + else { + const task::ExecutorPosition& executorPosition = fieldDestIter->second; + SendTupleTo(outputItem, executorPosition); + } + } +} + +void OutputDispatcher::SendTupleTo(OutputItem* outputItem, const task::ExecutorPosition& executorPosition) +{ + hurricane::base::NetAddress destAddress = executorPosition.GetSupervisor(); + std::string destIdentifier = destAddress.GetHost() + ":" + Int2String(destAddress.GetPort()); + std::string selfIdentifier = _selfAddress.GetHost() + ":" + Int2String(_selfAddress.GetPort()); + + if ( destIdentifier == selfIdentifier ) { + int executorIndex = executorPosition.GetExecutorIndex(); + int boltIndex = executorIndex - _selfSpoutCount; + + std::shared_ptr taskQueue = _selfTasks[boltIndex]; + TaskItem* taskItem = new TaskItem(outputItem->GetTaskIndex(), outputItem->GetTuple()); + taskQueue->Push(taskItem); + } + else { + std::map::iterator commandClientPair = + _commandClients.find(destIdentifier); + if ( commandClientPair == _commandClients.end() ) { + util::NetConnector* connector = new util::NetConnector(destAddress); + message::CommandClient* commandClient = new message::CommandClient(connector); + _commandClients.insert({destIdentifier, commandClient}); + + commandClientPair = _commandClients.find(destIdentifier); + } + + message::CommandClient* commandClient = commandClientPair->second; + + commandClient->GetConnector()->Connect([ + outputItem, commandClient, destIdentifier, executorPosition, this] { + hurricane::message::Command command(hurricane::message::Command::Type::SendTuple); + + base::Variants commandVariants; + _selfAddress.Serialize(commandVariants); + executorPosition.Serialize(commandVariants); + outputItem->GetTuple().Serialize(commandVariants); + + command.AddArguments(commandVariants); + + try { + commandClient->SendCommand(command, + [destIdentifier, this](const hurricane::message::Response& response) -> void { + if ( response.GetStatus() == hurricane::message::Response::Status::Successful ) { + } + else { + std::cout << "Send to " << destIdentifier << " failed." << std::endl; + } + }); + } + catch ( util::SocketException& e ) { +// std::cout << e.what() << std::endl; + } + }); + } +} + +void OutputDispatcher::AskField(TaskPathName taskPathName, + const std::string& fieldValue, OutputDispatcher::AskFieldCallback callback) +{ + _nimbusClient->Connect([taskPathName, fieldValue, callback, this]() { + hurricane::message::Command command(hurricane::message::Command::Type::AskField); + command.AddArgument(taskPathName.first); + command.AddArgument(taskPathName.second); + command.AddArgument(fieldValue); + + _nimbusClient->SendCommand(command, [callback](const hurricane::message::Response& response) { + task::ExecutorPosition destination; + const base::Variants respArguments = response.GetArguments(); + + base::Variants::const_iterator argIter = respArguments.cbegin(); + destination.Deserialize(argIter); + + callback(destination); + }); + }); +} + +} +} diff --git a/src/hurricane/message/Command.cpp b/src/hurricane/message/Command.cpp index 7c472a0..c8db121 100755 --- a/src/hurricane/message/Command.cpp +++ b/src/hurricane/message/Command.cpp @@ -13,7 +13,7 @@ namespace hurricane { } _arguments = dataPackage.GetVariants(); - _type = _arguments[0].GetIntValue(); + _type = _arguments[0].GetInt32Value(); _arguments.erase(_arguments.begin()); } @@ -37,7 +37,7 @@ namespace hurricane { } _arguments = dataPackage.GetVariants(); - _status = _arguments[0].GetIntValue(); + _status = _arguments[0].GetInt32Value(); _arguments.erase(_arguments.begin()); } @@ -52,4 +52,4 @@ namespace hurricane { return dataPackage.Serialize(); } } -} \ No newline at end of file +} diff --git a/src/hurricane/message/CommandClient.cpp b/src/hurricane/message/CommandClient.cpp index c9a96a9..bc9c3bc 100755 --- a/src/hurricane/message/CommandClient.cpp +++ b/src/hurricane/message/CommandClient.cpp @@ -10,13 +10,17 @@ namespace hurricane { if ( _connector ) { delete _connector; _connector = nullptr; - } - } + } + } + + void CommandClient::Connect(CommandClient::ConnectCallback callback) + { + this->_connector->Connect(callback); + } void CommandClient::SendCommand(const Command& command, SendCommandCallback callback) { try { - hurricane::base::ByteArray commandBytes = command.Serialize(); - std::cout << commandBytes.size() << std::endl; + hurricane::base::ByteArray commandBytes = command.Serialize(); _connector->SendAndReceive(commandBytes.data(), commandBytes.size(), [callback](char* resultBuffer, int32_t readSize) { hurricane::message::Response response; @@ -32,4 +36,4 @@ namespace hurricane { } } } -} \ No newline at end of file +} diff --git a/src/hurricane/service/Nimbus.cpp b/src/hurricane/service/Nimbus.cpp index 2029ace..94e4965 100755 --- a/src/hurricane/service/Nimbus.cpp +++ b/src/hurricane/service/Nimbus.cpp @@ -1,91 +1,527 @@ #include "hurricane/service/Nimbus.h" +#include "hurricane/util/NetConnector.h" +#include "hurricane/message/CommandClient.h" #include "hurricane/util/Configuration.h" #include "hurricane/topology/Topology.h" +#include "hurricane/topology/TopologyLoader.h" #include "sample/wordcount/WordCountTopology.h" #include +#include +#include +#include +#include +#include namespace hurricane { namespace service { Nimbus::Nimbus(const hurricane::base::NetAddress& host) : CommandServer(new hurricane::util::NetListener(host)), - _nimbusHost(host) { + _nimbusHost(host), + _supervisorCount(0) { OnConnection(std::bind(&Nimbus::OnConnect, this, std::placeholders::_1)); OnCommand(hurricane::message::Command::Type::Join, this, &Nimbus::OnJoin); + OnCommand(hurricane::message::Command::Type::AskField, this, &Nimbus::OnAskField); } Nimbus::Nimbus(const hurricane::util::Configuration& configuration) : Nimbus(hurricane::base::NetAddress( configuration.GetProperty("nimbus.host"), - configuration.GetIntegerProperty("nimbus.port"))) { + configuration.GetIntegerProperty("nimbus.port"))) { + _supervisorCount = configuration.GetIntegerProperty("nimbus.supervisor.count"); + _configuration.reset(new hurricane::util::Configuration(configuration)); + + std::cout << "Need supervisors: " << _supervisorCount << std::endl; + } + + void Nimbus::OnConnect(SupervisorContext* context) { } - void Nimbus::OnConnect(SupervisorContext* context) { - } - - void Nimbus::OnJoin(SupervisorContext* context, const hurricane::message::Command& command, - hurricane::message::CommandServer::Responser responser) { - std::string joinerType = command.GetArgument(0).GetStringValue(); - - std::cout << "Join node: " << joinerType << std::endl; - - SupervisorContext supervisorContext = SupervisorContext::FromVariants(command.GetArguments().cbegin() + 1); + void Nimbus::OnJoin(SupervisorContext* context, const hurricane::message::Command& command, + hurricane::message::CommandServer::Responser responser) { + std::string joinerType = command.GetArgument(0).GetStringValue(); + std::string supervisorHost = command.GetArgument(1).GetStringValue(); + int supervisorPort = command.GetArgument(2).GetInt32Value(); + + std::cout << "Join node: " << joinerType << std::endl; + + SupervisorContext supervisorContext; + base::Variants::const_iterator currentIterator = command.GetArguments().cbegin() + 3; + supervisorContext.Deserialize(currentIterator); std::cout << "Supervisor name: " << supervisorContext.GetId() << std::endl; + std::cout << "Host: " << supervisorHost << std::endl; + std::cout << "Port: " << supervisorPort << std::endl; std::cout << "Spout count: " << supervisorContext.GetSpoutCount() << std::endl; std::cout << "Bolt count: " << supervisorContext.GetBoltCount() << std::endl; std::cout << "Task info count: " << supervisorContext.GetTaskInfos().size() << std::endl; std::cout << "Free spout count: " << supervisorContext.GetFreeSpouts().size() << std::endl; std::cout << "Free bolt count: " << supervisorContext.GetFreeBolts().size() << std::endl; std::cout << "Busy spout count: " << supervisorContext.GetBusySpouts().size() << std::endl; - std::cout << "Busy bolt count: " << supervisorContext.GetBusyBolts().size() << std::endl; - - _supervisors.push_back(supervisorContext); - - if ( _supervisors.size() == 2 ) { - topology::Topology* topology = GetTopology(); - SubmitTopology(topology); - } - - hurricane::message::Response response(hurricane::message::Response::Status::Successful); - response.AddArgument({ "nimbus" }); - + std::cout << "Busy bolt count: " << supervisorContext.GetBusyBolts().size() << std::endl; + + supervisorContext.SetNetAddress(hurricane::base::NetAddress( + supervisorHost, supervisorPort)); + supervisorContext.PrepareTaskInfos(); + _supervisors.push_back(supervisorContext); + + // Response + hurricane::message::Response response(hurricane::message::Response::Status::Successful); + response.AddArgument({ "nimbus" }); + responser(response); - } - void Nimbus::SubmitTopology(hurricane::topology::Topology* topology) { - std::cout << "Submit topology: " << topology->GetName() << std::endl; + // Initialize command clients + hurricane::base::NetAddress supervisorAddress(supervisorHost, + supervisorPort); + hurricane::util::NetConnector* supervisorConnector = + new hurricane::util::NetConnector(supervisorAddress); + hurricane::message::CommandClient* supervisorCommandClient = + new hurricane::message::CommandClient(supervisorConnector); + + _supervisorClients.insert({supervisorContext.GetId(), + std::shared_ptr(supervisorCommandClient)}); + + SendHeartbeat(supervisorContext.GetId()); + + // Initialize topology + if ( _supervisors.size() == _supervisorCount ) { + std::string topologyName = _configuration->GetProperty("topology.name"); + hurricane::topology::Topology* topology = + hurricane::topology::TopologyLoader::GetInstance().GetTopology(topologyName).get(); + SubmitTopology(topology); + } + } + + void Nimbus::OnAskField(SupervisorContext* context, const hurricane::message::Command& command, + hurricane::message::CommandServer::Responser responser) + { + std::string sourceTaskName = command.GetArgument(0).GetStringValue(); + std::string destTaskName = command.GetArgument(1).GetStringValue(); + TaskPathName taskPathName = { sourceTaskName, destTaskName }; + std::string fieldValue = command.GetArgument(2).GetStringValue(); - const std::map& spoutDeclarers = topology->GetSpoutDeclarers(); - const std::map& boltDeclarers = topology->GetBoltDeclarers(); + auto taskPairIter = _fieldsDestinations.find(taskPathName); + if ( taskPairIter == _fieldsDestinations.end() ) { + _fieldsDestinations.insert({ taskPathName, std::map() }); + taskPairIter = _fieldsDestinations.find(taskPathName); + } + + std::map& destinations = taskPairIter->second; + auto destinationPairIter = destinations.find(fieldValue); + if ( destinationPairIter == destinations.end() ) { + std::vector& candidates = _fieldsCandidates[taskPathName]; + int positionIndex = rand() % candidates.size(); + + destinations.insert({fieldValue, candidates[positionIndex]}); + destinationPairIter = destinations.find(fieldValue); + } + + task::ExecutorPosition destination = destinationPairIter->second; + base::Variants destinationVariants; + destination.Serialize(destinationVariants); + + hurricane::message::Response response(hurricane::message::Response::Status::Successful); + response.AddArguments(destinationVariants); + + responser(response); + } + std::list Nimbus::GetAllSpoutTasks( + const std::map& spoutDeclarers, + hurricane::topology::Topology* topology) + { + std::list originSpoutTasks; for ( const auto& spoutPair : spoutDeclarers ) { hurricane::spout::SpoutDeclarer spoutDeclarer = spoutPair.second; std::cout << "Spout " << spoutDeclarer.GetTaskName() << std::endl; std::cout << "ParallismHint: " << spoutDeclarer.GetParallismHint() << std::endl; - - // Allocate spout tasks - for ( const SupervisorContext& supervisorContext : _supervisors ) { - if ( supervisorContext.GetFreeSpouts().size() ) { + int parallismHint = spoutDeclarer.GetParallismHint(); + for ( int taskIndex = 0; taskIndex != parallismHint; ++ taskIndex ) { + hurricane::task::TaskInfo taskInfo; + taskInfo.SetTopologyName(topology->GetName()); + taskInfo.SetTaskName(spoutDeclarer.GetTaskName()); + + originSpoutTasks.push_back(taskInfo); + } + } + + return originSpoutTasks; + } + + std::map> + Nimbus::AllocateSpoutTasks(std::list& originSpoutTasks) + { + std::map> nameToSpoutTasks; + // Allocate task for every supervisor + for ( SupervisorContext& supervisorContext : _supervisors ) { + if ( !originSpoutTasks.size() ) { + break; + } + + while ( true ) { + if ( !originSpoutTasks.size() ) { + break; + } + + // If useNextSpout return -1, the spout slots is used up + int spoutIndex = supervisorContext.useNextSpout(); + if ( spoutIndex == -1 ) { + break; + } + + // Put the spout task into spout slot + hurricane::task::TaskInfo taskInfo = originSpoutTasks.front(); + taskInfo.SetSupervisorContext(&supervisorContext); + taskInfo.SetExecutorIndex(supervisorContext.GetExecutorIndex( + SupervisorContext::ExecutorType::Spout, spoutIndex)); + originSpoutTasks.pop_front(); + supervisorContext.SetSpoutTaskInfo(spoutIndex, taskInfo); + // Insert the spout task pointer into mapper + std::string taskName = taskInfo.GetTaskName(); + auto spoutTasksPair = nameToSpoutTasks.find(taskName); + if ( spoutTasksPair == nameToSpoutTasks.end() ) { + nameToSpoutTasks.insert({taskName, std::vector()}); + spoutTasksPair = nameToSpoutTasks.find(taskName); } + + spoutTasksPair->second.push_back(&(supervisorContext.GetSpoutTaskInfo(spoutIndex))); } } + return nameToSpoutTasks; + } + + std::list Nimbus::GetAllBoltTasks(hurricane::topology::Topology* topology, + const std::map& boltDeclarers) + { + std::list originBoltTasks; for ( const auto& boltPair : boltDeclarers ) { hurricane::bolt::BoltDeclarer boltDeclarer = boltPair.second; std::cout << "Bolt " << boltDeclarer.GetTaskName() << std::endl; std::cout << "Source: " << boltDeclarer.GetSourceTaskName() << std::endl; std::cout << "ParallismHint: " << boltDeclarer.GetParallismHint() << std::endl; - // Allocate bolt tasks - for ( const SupervisorContext& supervisorContext : _supervisors ) { - if ( supervisorContext.GetFreeBolts().size() ) { + int parallismHint = boltDeclarer.GetParallismHint(); + for ( int taskIndex = 0; taskIndex != parallismHint; ++ taskIndex ) { + hurricane::task::TaskInfo taskInfo; + taskInfo.SetTopologyName(topology->GetName()); + taskInfo.SetTaskName(boltDeclarer.GetTaskName()); + + originBoltTasks.push_back(taskInfo); + } + } + + return originBoltTasks; + } + + std::map> + Nimbus::AllocateBoltTasks(std::list& originBoltTasks) + { + std::map> nameToBoltTasks; + // Allocate bolt tasks + for ( SupervisorContext& supervisorContext : _supervisors ) { + if ( !originBoltTasks.size() ) { + break; + } + + while ( true ) { + if ( !originBoltTasks.size() ) { + break; + } + + // If useNextBolt return -1, the bolt slots is used up + int boltIndex = supervisorContext.useNextBolt(); + if ( boltIndex == -1 ) { + break; + } + + // Put the bolt task into bolt slot + hurricane::task::TaskInfo taskInfo = originBoltTasks.front(); + taskInfo.SetSupervisorContext(&supervisorContext); + taskInfo.SetExecutorIndex(supervisorContext.GetExecutorIndex( + SupervisorContext::ExecutorType::Bolt, boltIndex)); + originBoltTasks.pop_front(); + supervisorContext.SetBoltTaskInfo(boltIndex, taskInfo); + // Insert the bolt task pointer into mapper + std::string taskName = taskInfo.GetTaskName(); + auto boltTasksPair = nameToBoltTasks.find(taskName); + if ( boltTasksPair == nameToBoltTasks.end() ) { + nameToBoltTasks.insert({taskName, std::vector()}); + boltTasksPair = nameToBoltTasks.find(taskName); } + + boltTasksPair->second.push_back(&(supervisorContext.GetBoltTaskInfo(boltIndex))); + } + } + + return nameToBoltTasks; + } + + std::vector Nimbus::FindTask( + const std::map>& nameToBoltTasks, + const std::map>& nameToSpoutTasks, + const std::string& sourceTaskName) + { + auto spoutTaskPair = nameToSpoutTasks.find(sourceTaskName); + if ( spoutTaskPair != nameToSpoutTasks.end() ) { + return spoutTaskPair->second; + } + + auto boltTaskPair = nameToBoltTasks.find(sourceTaskName); + if ( boltTaskPair != nameToBoltTasks.end() ) { + return boltTaskPair->second; + } + + return std::vector(); + } + + std::vector Nimbus::FindTask( + const std::map>& nameToBoltTasks, + const std::string& sourceTaskName) + { + auto boltTaskPair = nameToBoltTasks.find(sourceTaskName); + if ( boltTaskPair != nameToBoltTasks.end() ) { + return boltTaskPair->second; + } + + return std::vector(); + } + + void Nimbus::ShowTaskInfos(const std::vector& taskInfos) + { + for ( const hurricane::task::TaskInfo& taskInfo : taskInfos ) { + if ( !taskInfo.GetSupervisorContext() ) { + continue; + } + + std::cout << " Supervisor: " << taskInfo.GetSupervisorContext()->GetId() << std::endl; + std::cout << " Exectuor index: " << taskInfo.GetExecutorIndex() << std::endl; + std::cout << " Paths: " << std::endl; + const std::list& paths = taskInfo.GetPaths(); + + for ( const hurricane::task::PathInfo& path : paths ) { + std::cout << " Path: " << std::endl; + int groupMethod = path.GetGroupMethod(); + std::cout << " Group method: " << groupMethod << std::endl; + if ( path.GetGroupMethod() == hurricane::task::PathInfo::GroupMethod::Global) { + std::cout << " Destination host: " << + path.GetDestinationExecutors()[0].GetSupervisor().GetHost() << std::endl; + std::cout << " Destination port: " << + path.GetDestinationExecutors()[0].GetSupervisor().GetPort() << std::endl; + std::cout << " Destination executor index: " << + path.GetDestinationExecutors()[0].GetExecutorIndex() << std::endl; + } + } + } + } + + void Nimbus::SyncWithSupervisors() + { + for ( SupervisorContext& supervisorContext : _supervisors ) { + std::string supervisorId = supervisorContext.GetId(); + std::cout << "Sync meta data with supervisr: " << supervisorId; + std::shared_ptr supervisorClient = + _supervisorClients[supervisorId]; + + supervisorClient->GetConnector()->Connect([supervisorId, supervisorClient, &supervisorContext, this] { + hurricane::message::Command command(hurricane::message::Command::Type::SyncMetadata); + + // 1 means Nimbus to Supervisor + // 2 means Supervisor to Nimbus + command.AddArgument({ 1 }); + + base::Variants supervisorContextVariants; + supervisorContext.Serialize(supervisorContextVariants); + command.AddArguments(supervisorContextVariants); + supervisorClient->SendCommand(command, + [supervisorId, this](const hurricane::message::Response& response) -> void { + if ( response.GetStatus() == hurricane::message::Response::Status::Successful ) { + std::cout << "Sync with " << supervisorId << " successfully." << std::endl; + } + else { + std::cout << "Sync with " << supervisorId << " failed." << std::endl; + } + }); + }); + } + } + + void Nimbus::ShowSupervisorTaskInfos() + { + std::cout << std::endl << "================ Allocate result ================" << std::endl; + for ( SupervisorContext& supervisorContext : _supervisors ) { + std::cout << supervisorContext.GetId() << std::endl; + std::cout << " Host: " << supervisorContext.GetNetAddress().GetHost() << std::endl; + std::cout << " Port: " << supervisorContext.GetNetAddress().GetPort() << std::endl; + + std::cout << " Tasks: " << std::endl; + const std::vector& taskInfos = + supervisorContext.GetTaskInfos(); + ShowTaskInfos(taskInfos); + } + } + + void Nimbus::CalculateTaskPaths( + const std::map>& nameToBoltTasks, + const std::map& boltDeclarers, + const std::map>& nameToSpoutTasks) + { + for ( const auto& boltPair : boltDeclarers ) { + hurricane::bolt::BoltDeclarer boltDeclarer = boltPair.second; + // No setted source task + if ( boltDeclarer.GetSourceTaskName().empty() ) { + continue; + } + + std::string sourceTaskName = boltDeclarer.GetSourceTaskName(); + std::vector sourceTasks = + FindTask(nameToBoltTasks, nameToSpoutTasks, sourceTaskName); + + std::string destTaskName = boltDeclarer.GetTaskName(); + std::vector destTasks = + FindTask(nameToBoltTasks, destTaskName); + + std::vector destExecutorPositions; + for ( hurricane::task::TaskInfo* destTask : destTasks ) { + destExecutorPositions.push_back(task::ExecutorPosition( + destTask->GetSupervisorContext()->GetNetAddress(), + destTask->GetExecutorIndex() + )); + } + + if ( boltDeclarer.GetGroupMethod() == task::TaskDeclarer::GroupMethod::Global ) { + for ( hurricane::task::TaskInfo* sourceTask : sourceTasks ) { + int destTaskIndex = rand() % destTasks.size(); + hurricane::task::TaskInfo* destTask = destTasks[destTaskIndex]; + + hurricane::task::PathInfo pathInfo; + pathInfo.SetGroupMethod(hurricane::task::PathInfo::GroupMethod::Global); + pathInfo.SetDestinationTask(destTask->GetTaskName()); + pathInfo.SetDestinationExecutors({task::ExecutorPosition( + destTask->GetSupervisorContext()->GetNetAddress(), + destTask->GetExecutorIndex() + )}); + + sourceTask->AddPath(pathInfo); + } + } + else if ( boltDeclarer.GetGroupMethod() == task::TaskDeclarer::GroupMethod::Field ) { + // Resolve the destination by field when run task. + for ( hurricane::task::TaskInfo* sourceTask : sourceTasks ) { + hurricane::task::PathInfo pathInfo; + pathInfo.SetGroupMethod(hurricane::task::PathInfo::GroupMethod::Field); + pathInfo.SetDestinationTask(destTaskName); + pathInfo.SetFieldName(boltDeclarer.GetGroupField()); + + sourceTask->AddPath(pathInfo); + } + + TaskPathName taskPathName = { sourceTaskName, destTaskName }; + _fieldsCandidates[taskPathName] = destExecutorPositions; + } + else if ( boltDeclarer.GetGroupMethod() == task::TaskDeclarer::GroupMethod::Random ) { + // Resolve the destination by field when run task. + for ( hurricane::task::TaskInfo* sourceTask : sourceTasks ) { + hurricane::task::PathInfo pathInfo; + pathInfo.SetGroupMethod(hurricane::task::PathInfo::GroupMethod::Random); + pathInfo.SetDestinationTask(destTaskName); + pathInfo.SetDestinationExecutors(destExecutorPositions); + + sourceTask->AddPath(pathInfo); + } + } + else { + std::cerr << "Unsupported group method occured" << std::endl; + exit(EXIT_FAILURE); + } + } + } + + void Nimbus::ShowSupervisorMetadata() + { + std::cout << std::endl << "================ Supervisor metadata ================" << std::endl; + for ( SupervisorContext& supervisorContext : _supervisors ) { + std::cout << "Supervisor name: " << supervisorContext.GetId() << std::endl; + std::cout << " Spout count: " << supervisorContext.GetSpoutCount() << std::endl; + std::cout << " Bolt count: " << supervisorContext.GetBoltCount() << std::endl; + std::cout << " Task info count: " << supervisorContext.GetTaskInfos().size() << std::endl; + std::cout << " Free spout count: " << supervisorContext.GetFreeSpouts().size() << std::endl; + std::cout << " Free bolt count: " << supervisorContext.GetFreeBolts().size() << std::endl; + std::cout << " Busy spout count: " << supervisorContext.GetBusySpouts().size() << std::endl; + std::cout << " Busy bolt count: " << supervisorContext.GetBusyBolts().size() << std::endl; + } + } + + void Nimbus::SubmitTopology(hurricane::topology::Topology* topology) { + std::cout << "Submit topology: " << topology->GetName() << std::endl; + + const std::map& spoutDeclarers = + topology->GetSpoutDeclarers(); + const std::map& boltDeclarers = + topology->GetBoltDeclarers(); + + // Allocate task and send to supervisor + std::list originSpoutTasks = + GetAllSpoutTasks(spoutDeclarers, topology); + std::map> nameToSpoutTasks = + AllocateSpoutTasks(originSpoutTasks); + + std::list originBoltTasks = + GetAllBoltTasks(topology, boltDeclarers); + std::map> nameToBoltTasks = + AllocateBoltTasks(originBoltTasks); + + CalculateTaskPaths(nameToBoltTasks, boltDeclarers, nameToSpoutTasks); + ShowSupervisorTaskInfos(); + ShowSupervisorMetadata(); + SyncWithSupervisors(); + } + + const int MAX_HEARTBEAT_FAILED_TIMES = 5; + void Nimbus::SendHeartbeat(const std::string supervisorId) + { + std::cout << "Sending heartbeat to " << supervisorId << std::endl; + + int sendTimes = 0; + while ( true ) { + try { + std::shared_ptr commandClient = + _supervisorClients.at(supervisorId); + + commandClient->GetConnector()->Connect([commandClient, supervisorId, this] { + std::cout << "Connected to " << supervisorId << std::endl; + hurricane::message::Command command(hurricane::message::Command::Type::Heartbeat); + + commandClient->SendCommand(command, + [supervisorId, this](const hurricane::message::Response& response) -> void { + if ( response.GetStatus() == hurricane::message::Response::Status::Successful ) { + std::cout << supervisorId << " alived." << std::endl; + } + else { + std::cout << supervisorId << " dead." << std::endl; + } + }); + }); + + break; + } + catch ( const std::exception& e ) { + std::cerr << "Error in sending heartbeat to " << supervisorId << std::endl; + std::cerr << e.what() << std::endl; + + sendTimes ++; + std::cout << "Sendtimes: " << sendTimes << std::endl; + if ( sendTimes >= MAX_HEARTBEAT_FAILED_TIMES ) { + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } } } } -} \ No newline at end of file +} diff --git a/src/hurricane/service/Supervisor.cpp b/src/hurricane/service/Supervisor.cpp index 032c7e2..4b9c3d7 100755 --- a/src/hurricane/service/Supervisor.cpp +++ b/src/hurricane/service/Supervisor.cpp @@ -2,61 +2,334 @@ #include "hurricane/message/CommandClient.h" #include "hurricane/util/NetConnector.h" #include "hurricane/util/Configuration.h" +#include "hurricane/topology/Topology.h" +#include "hurricane/topology/TopologyLoader.h" +#include "hurricane/task/SpoutExecutor.h" +#include "hurricane/task/BoltExecutor.h" +#include "hurricane/spout/ISpout.h" +#include "hurricane/bolt/IBolt.h" +#include "hurricane/collector/OutputCollector.h" +#include "hurricane/collector/OutputQueue.h" +#include "hurricane/collector/TaskQueue.h" namespace hurricane { - namespace service { - Supervisor::Supervisor(const hurricane::util::Configuration& configuration) : + namespace service { + + Supervisor::Supervisor(const hurricane::util::Configuration& configuration) : CommandServer(new hurricane::util::NetListener(hurricane::base::NetAddress( configuration.GetProperty("supervisor.host"), - configuration.GetIntegerProperty("supervisor.port")))) { - _supervisorConfiguration.reset(new hurricane::util::Configuration(configuration)); - - hurricane::base::NetAddress nimbusAddress(configuration.GetProperty("nimbus.host"), - configuration.GetIntegerProperty("nimbus.port")); - _nimbusConnector = new hurricane::util::NetConnector(nimbusAddress); - _nimbusClient = new hurricane::message::CommandClient(_nimbusConnector); - - _name = configuration.GetProperty("supervisor.name"); - InitSelfContext(); - OnConnection(std::bind(&Supervisor::OnConnect, this, std::placeholders::_1)); - } - - void Supervisor::OnConnect(SupervisorContext* context) { - } - - void Supervisor::JoinNimbus(JoinNimbusCallback callback) { - hurricane::message::CommandClient* commandClient = _nimbusClient; - - _nimbusConnector->Connect([commandClient, callback, this]() { - hurricane::message::Command command(hurricane::message::Command::Type::Join); - command.AddArgument({ "supervisor" }); - std::vector context = _selfContext->ToVariants(); - command.AddArguments(context); - - commandClient->SendCommand(command, [callback](const hurricane::message::Response& response) { - callback(response); - }); - }); - } - - void Supervisor::InitSelfContext() { - this->_selfContext.reset(new SupervisorContext); - _selfContext->SetId(_name); - _selfContext->SetSpoutCount(_supervisorConfiguration->GetIntegerProperty("supervisor.spout.num")); - _selfContext->SetBoltCount(_supervisorConfiguration->GetIntegerProperty("supervisor.bolt.num")); - _selfContext->SetTaskInfos(std::vector(_selfContext->GetSpoutCount() + _selfContext->GetBoltCount())); - - std::set freeSpouts; - for ( int spoutIndex = 0; spoutIndex != _selfContext->GetSpoutCount(); ++ spoutIndex ) { - freeSpouts.insert(spoutIndex); + configuration.GetIntegerProperty("supervisor.port")))), + _host(configuration.GetProperty("supervisor.host")), + _port(configuration.GetIntegerProperty("supervisor.port")) { + _supervisorConfiguration.reset(new hurricane::util::Configuration(configuration)); + _name = configuration.GetProperty("supervisor.name"); + + InitNimbusConnector(); + InitSelfContext(); + ReserveExecutors(); + InitEvents(); + } + + void Supervisor::InitNimbusConnector() + { + hurricane::base::NetAddress nimbusAddress(_supervisorConfiguration->GetProperty("nimbus.host"), + _supervisorConfiguration->GetIntegerProperty("nimbus.port")); + _nimbusConnector = new hurricane::util::NetConnector(nimbusAddress); + _nimbusClient = new hurricane::message::CommandClient(_nimbusConnector); + } + + void Supervisor::ReserveExecutors() + { + _spoutExecutors.resize(_supervisorConfiguration->GetIntegerProperty("supervisor.spout.num")); + _boltExecutors.resize(_supervisorConfiguration->GetIntegerProperty("supervisor.bolt.num")); + _spoutCollectors.resize(_supervisorConfiguration->GetIntegerProperty("supervisor.spout.num")); + _boltCollectors.resize(_supervisorConfiguration->GetIntegerProperty("supervisor.bolt.num")); + _boltTaskQueues.resize(_supervisorConfiguration->GetIntegerProperty("supervisor.bolt.num")); + + for ( auto& boltTask : _boltTaskQueues ) { + boltTask.reset(new collector::TaskQueue); + } + + _outputDispatcher.SetQueue(std::shared_ptr( + new collector::OutputQueue())); + _outputDispatcher.SetSelfAddress(hurricane::base::NetAddress(_host, _port)); + _outputDispatcher.SetSelfTasks(_boltTaskQueues); + _outputDispatcher.SetSelfSpoutCount(_spoutExecutors.size()); + + hurricane::base::NetAddress nimbusAddress(_supervisorConfiguration->GetProperty("nimbus.host"), + _supervisorConfiguration->GetIntegerProperty("nimbus.port")); + _nimbusConnector = new hurricane::util::NetConnector(nimbusAddress); + _nimbusClient = new hurricane::message::CommandClient(_nimbusConnector); + _outputDispatcher.SetNimbusClient(_nimbusClient); + + _outputDispatcher.Start(); + } + + void Supervisor::InitEvents() + { + OnConnection(std::bind(&Supervisor::OnConnect, this, std::placeholders::_1)); + OnCommand(hurricane::message::Command::Type::Heartbeat, this, &Supervisor::OnHeartbeat); + OnCommand(hurricane::message::Command::Type::SyncMetadata, this, &Supervisor::OnSyncMetadata); + OnCommand(hurricane::message::Command::Type::SendTuple, this, &Supervisor::OnSendTuple); + } + + void Supervisor::InitTaskFieldsMap() + { + const std::map& spoutDeclarers = + _topology->GetSpoutDeclarers(); + for ( const auto& spoutDeclarerPair : spoutDeclarers ) { + const spout::SpoutDeclarer& spoutDeclarer = spoutDeclarerPair.second; + + _taskFields[spoutDeclarer.GetTaskName()] = &spoutDeclarer.GetFields(); + _taskFieldsMap[spoutDeclarer.GetTaskName()] = &spoutDeclarer.GetFieldsMap(); + } + + const std::map& boltDeclarers = + _topology->GetBoltDeclarers(); + for ( const auto& boltDeclarerPair : boltDeclarers ) { + const bolt::BoltDeclarer& boltDeclarer = boltDeclarerPair.second; + + _taskFields[boltDeclarer.GetTaskName()] = &boltDeclarer.GetFields(); + _taskFieldsMap[boltDeclarer.GetTaskName()] = &boltDeclarer.GetFieldsMap(); + } + + _outputDispatcher.SetTaskFields(_taskFields); + _outputDispatcher.SetTaskFieldsMap(_taskFieldsMap); + } + + void Supervisor::OnConnect(SupervisorContext* context) { + } + + void Supervisor::JoinNimbus(JoinNimbusCallback callback) { + hurricane::message::CommandClient* commandClient = _nimbusClient; + + _nimbusConnector->Connect([commandClient, callback, this]() { + hurricane::message::Command command(hurricane::message::Command::Type::Join); + command.AddArgument({ "supervisor" }); + command.AddArgument({ this->_host }); + command.AddArgument({ this->_port }); + std::vector context; + _selfContext->Serialize(context); + command.AddArguments(context); + + commandClient->SendCommand(command, [callback](const hurricane::message::Response& response) { + callback(response); + }); + }); + } + + void Supervisor::OnHeartbeat(SupervisorContext* context, const message::Command& command, + hurricane::message::CommandServer::Responser responser) + { + hurricane::message::Response response(hurricane::message::Response::Status::Successful); + response.AddArgument({ _name }); + + responser(response); + } + + void Supervisor::OnSyncMetadata(SupervisorContext* context, const message::Command& command, + message::CommandServer::Responser responser) + { + const std::vector& arguments = command.GetArguments(); + + int syncMethod = arguments[0].GetInt32Value(); + if ( syncMethod != 1 ) { + hurricane::message::Response response(hurricane::message::Response::Status::Failed); + responser(response); + + return; + } + + hurricane::message::Response response(hurricane::message::Response::Status::Successful); + base::Variants::const_iterator currentIterator = arguments.cbegin() + 1; + _selfContext->Deserialize(currentIterator); + + OwnSupervisorTasks(); + _outputDispatcher.SetTaskInfos(_selfContext->GetTaskInfos()); + + ShowSupervisorMetadata(); + ShowTaskInfos(); + + std::string topologyName = _supervisorConfiguration->GetProperty("topology.name"); + _topology = hurricane::topology::TopologyLoader::GetInstance().GetTopology(topologyName); + + InitTaskFieldsMap(); + InitExecutors(); + + responser(response); + } + + void Supervisor::OnSendTuple(SupervisorContext* context, const message::Command& command, + message::CommandServer::Responser responser) + { + const base::Variants& arguments = command.GetArguments(); + base::Variants::const_iterator it = arguments.cbegin(); + + base::NetAddress sourceAddress; + sourceAddress.Deserialize(it); + + task::ExecutorPosition destination; + destination.Deserialize(it); + + base::Tuple tuple; + tuple.Deserialize(it); + tuple.SetFields(_taskFields[tuple.GetSourceTask()]); + tuple.SetFieldsMap(_taskFieldsMap[tuple.GetSourceTask()]); + + int executorIndex = destination.GetExecutorIndex(); + int boltIndex = executorIndex - _selfContext->GetSpoutCount(); + + std::shared_ptr taskQueue = _boltTaskQueues[boltIndex]; + collector::TaskItem* taskItem = + new collector::TaskItem(executorIndex, tuple); + taskQueue->Push(taskItem); + + hurricane::message::Response response(hurricane::message::Response::Status::Successful); + responser(response); + } + + void Supervisor::InitSelfContext() { + this->_selfContext.reset(new SupervisorContext); + _selfContext->SetId(_name); + _selfContext->SetSpoutCount(_supervisorConfiguration->GetIntegerProperty("supervisor.spout.num")); + _selfContext->SetBoltCount(_supervisorConfiguration->GetIntegerProperty("supervisor.bolt.num")); + _selfContext->SetTaskInfos(std::vector(_selfContext->GetSpoutCount() + _selfContext->GetBoltCount())); + + std::set freeSpouts; + for ( int spoutIndex = 0; spoutIndex != _selfContext->GetSpoutCount(); ++ spoutIndex ) { + freeSpouts.insert(spoutIndex); + } + _selfContext->SetFreeSpouts(freeSpouts); + + std::set freeBolts; + for ( int boltIndex = 0; boltIndex != _selfContext->GetBoltCount(); ++ boltIndex ) { + freeBolts.insert(boltIndex); + } + _selfContext->SetFreeBolts(freeBolts); + } + + void Supervisor::InitSpoutExecutors() + { + std::cout << "Init spout executors" << std::endl; + const std::map& spoutDeclarers = + _topology->GetSpoutDeclarers(); + std::set busySpouts = _selfContext->GetBusySpouts(); + for ( int spoutIndex : busySpouts ) { + hurricane::task::TaskInfo& spoutTask = _selfContext->GetSpoutTaskInfo(spoutIndex); + std::string taskName = spoutTask.GetTaskName(); + const hurricane::spout::SpoutDeclarer& spoutDeclarer = spoutDeclarers.at(taskName); + + std::shared_ptr outputQueue = _outputDispatcher.GetQueue(); + collector::OutputCollector* collector = new collector::OutputCollector(spoutIndex, + taskName, outputQueue); + _spoutCollectors[spoutIndex].reset(collector); + + spout::ISpout* spout = spoutDeclarer.GetSpout()->Clone(); + spout->Prepare(_spoutCollectors[spoutIndex]); + + std::shared_ptr spoutExecutor(new task::SpoutExecutor); + spoutExecutor->SetSpout(spout); + int flowParam = _supervisorConfiguration->GetIntegerProperty("spout.flow.param"); + spoutExecutor->SetFlowParam(flowParam); + _spoutExecutors[spoutIndex] = spoutExecutor; + } + } + + void Supervisor::InitBoltExecutors() + { + std::cout << "Init bolt executors" << std::endl; + const std::map& boltDeclarers = + _topology->GetBoltDeclarers(); + std::set busyBolts = _selfContext->GetBusyBolts(); + int spoutCount = _selfContext->GetSpoutCount(); + for ( int boltIndex : busyBolts ) { + std::cout << boltIndex << std::endl; + + hurricane::task::TaskInfo& boltTask = _selfContext->GetBoltTaskInfo(boltIndex); + std::string taskName = boltTask.GetTaskName(); + const hurricane::bolt::BoltDeclarer& boltDeclarer = boltDeclarers.at(taskName); + + std::shared_ptr outputQueue = _outputDispatcher.GetQueue(); + collector::OutputCollector* collector = new collector::OutputCollector( + spoutCount + boltIndex, taskName, outputQueue); + _boltCollectors[boltIndex].reset(collector); + + bolt::IBolt* bolt = boltDeclarer.GetBolt()->Clone(); + bolt->Prepare(_boltCollectors[boltIndex]); + + std::shared_ptr boltExecutor(new task::BoltExecutor); + _boltExecutors[boltIndex] = boltExecutor; + boltExecutor->SetTaskQueue(_boltTaskQueues[boltIndex]); + boltExecutor->SetBolt(bolt); + } + } + + void Supervisor::InitExecutors() + { + InitSpoutExecutors(); + InitBoltExecutors(); + + std::set busyBolts = _selfContext->GetBusyBolts(); + std::set busySpouts = _selfContext->GetBusySpouts(); + + for ( int boltIndex : busyBolts ) { + _boltExecutors[boltIndex]->Start(); + } + + for ( int spoutIndex : busySpouts ) { + _spoutExecutors[spoutIndex]->Start(); + } + } + + void Supervisor::OwnSupervisorTasks() + { + std::vector& taskInfos = _selfContext->GetTaskInfos(); + for ( hurricane::task::TaskInfo& taskInfo : taskInfos ) { + taskInfo.SetSupervisorContext(_selfContext.get()); + } + } + + void Supervisor::ShowSupervisorMetadata() + { + std::cout << "Supervisor name: " << _selfContext->GetId() << std::endl; + std::cout << " Spout count: " << _selfContext->GetSpoutCount() << std::endl; + std::cout << " Bolt count: " << _selfContext->GetBoltCount() << std::endl; + std::cout << " Task info count: " << _selfContext->GetTaskInfos().size() << std::endl; + std::cout << " Free spout count: " << _selfContext->GetFreeSpouts().size() << std::endl; + std::cout << " Free bolt count: " << _selfContext->GetFreeBolts().size() << std::endl; + std::cout << " Busy spout count: " << _selfContext->GetBusySpouts().size() << std::endl; + std::cout << " Busy bolt count: " << _selfContext->GetBusyBolts().size() << std::endl; + } + + void Supervisor::ShowTaskInfos() + { + const std::vector& taskInfos = _selfContext->GetTaskInfos(); + for ( const hurricane::task::TaskInfo& taskInfo : taskInfos ) { + if ( !taskInfo.GetSupervisorContext() ) { + continue; } - _selfContext->SetFreeSpouts(freeSpouts); - std::set freeBolts; - for ( int boltIndex = 0; boltIndex != _selfContext->GetBoltCount(); ++ boltIndex ) { - freeBolts.insert(boltIndex); + std::cout << " Supervisor: " << taskInfo.GetSupervisorContext()->GetId() << std::endl; + std::cout << " Exectuor index: " << taskInfo.GetExecutorIndex() << std::endl; + std::cout << " Task name: " << taskInfo.GetTaskName() << std::endl; + std::cout << " Paths: " << std::endl; + const std::list& paths = taskInfo.GetPaths(); + + for ( const hurricane::task::PathInfo& path : paths ) { + std::cout << " Path: " << std::endl; + int groupMethod = path.GetGroupMethod(); + std::cout << " Group method: " << groupMethod << std::endl; + + if ( path.GetGroupMethod() == hurricane::task::PathInfo::GroupMethod::Global) { + std::cout << " Destination host: " << + path.GetDestinationExecutors()[0].GetSupervisor().GetHost() << std::endl; + std::cout << " Destination port: " << + path.GetDestinationExecutors()[0].GetSupervisor().GetPort() << std::endl; + std::cout << " Destination executor index: " << + path.GetDestinationExecutors()[0].GetExecutorIndex() << std::endl; + } } - _selfContext->SetFreeBolts(freeBolts); } - } -} \ No newline at end of file + } +} +} diff --git a/src/hurricane/service/SupervisorContext.cpp b/src/hurricane/service/SupervisorContext.cpp index 6dacfd3..308ae28 100755 --- a/src/hurricane/service/SupervisorContext.cpp +++ b/src/hurricane/service/SupervisorContext.cpp @@ -1,103 +1,39 @@ #include "hurricane/service/SupervisorContext.h" namespace hurricane { - namespace service { - SupervisorContext::SupervisorContext() : _spoutCount(0), _boltCount(0) { - } - - std::vector SupervisorContext::ToVariants() { - std::vector variants; - - variants.push_back({ _id }); - variants.push_back({ _spoutCount }); - variants.push_back({ _boltCount }); - - hurricane::base::Variants freeSpoutsVariants = hurricane::base::Variant::FromStdSet(_freeSpouts); - variants.insert(variants.end(), freeSpoutsVariants.begin(), freeSpoutsVariants.end()); - - hurricane::base::Variants freeBoltsVariants = hurricane::base::Variant::FromStdSet(_freeBolts); - variants.insert(variants.end(), freeBoltsVariants.begin(), freeBoltsVariants.end()); - - hurricane::base::Variants busySpoutsVariants = hurricane::base::Variant::FromStdSet(_busySpouts); - variants.insert(variants.end(), busySpoutsVariants.begin(), busySpoutsVariants.end()); - - hurricane::base::Variants busyBoltsVariants = hurricane::base::Variant::FromStdSet(_busyBolts); - variants.insert(variants.end(), busyBoltsVariants.begin(), busyBoltsVariants.end()); - - return variants; - } - - void SupervisorContext::ParseVariants(const std::vector& variants) { - _id = variants[0].GetStringValue(); - _spoutCount = variants[1].GetIntValue(); - _boltCount = variants[2].GetIntValue(); - - int32_t currentIndex = 3; - - int32_t freeSpoutsSize = variants[currentIndex].GetIntValue(); - currentIndex ++; - _freeSpouts = hurricane::base::Variant::ToStdSet(variants.cbegin() + currentIndex, variants.cbegin() + currentIndex + freeSpoutsSize); - currentIndex += freeSpoutsSize; - - int32_t freeBoltsSize = variants[currentIndex].GetIntValue(); - currentIndex ++; - _freeBolts = hurricane::base::Variant::ToStdSet(variants.cbegin() + currentIndex, variants.cbegin() + currentIndex + freeBoltsSize); - currentIndex += freeBoltsSize; - - int32_t busySpoutsSize = variants[currentIndex].GetIntValue(); - currentIndex ++; - _busySpouts = hurricane::base::Variant::ToStdSet(variants.cbegin() + currentIndex, variants.cbegin() + currentIndex + busySpoutsSize); - currentIndex += busySpoutsSize; - - int32_t busyBoltsSize = variants[currentIndex].GetIntValue(); - currentIndex ++; - _busyBolts = hurricane::base::Variant::ToStdSet(variants.cbegin() + currentIndex, variants.cbegin() + currentIndex + busyBoltsSize); - currentIndex += busyBoltsSize; - } - - void SupervisorContext::ParseVariants(std::vector::const_iterator begin) { - auto currentIterator = begin; - - _id = currentIterator->GetStringValue(); - currentIterator ++; - _spoutCount = currentIterator->GetIntValue(); - currentIterator ++; - _boltCount = currentIterator->GetIntValue(); - currentIterator ++; - - int32_t freeSpoutsSize = currentIterator->GetIntValue(); - currentIterator ++; - _freeSpouts = hurricane::base::Variant::ToStdSet(currentIterator, currentIterator + freeSpoutsSize); - currentIterator += freeSpoutsSize; - - int32_t freeBoltsSize = currentIterator->GetIntValue(); - currentIterator ++; - _freeBolts = hurricane::base::Variant::ToStdSet(currentIterator, currentIterator + freeBoltsSize); - currentIterator += freeBoltsSize; - - int32_t busySpoutsSize = currentIterator->GetIntValue(); - currentIterator ++; - _busySpouts = hurricane::base::Variant::ToStdSet(currentIterator, currentIterator + busySpoutsSize); - currentIterator += busySpoutsSize; - - int32_t busyBoltsSize = currentIterator->GetIntValue(); - currentIterator ++; - _busyBolts = hurricane::base::Variant::ToStdSet(currentIterator, currentIterator + busyBoltsSize); - currentIterator += busyBoltsSize; - } - - SupervisorContext SupervisorContext::FromVariants(std::vector::const_iterator begin) { - SupervisorContext context; - context.ParseVariants(begin); - - return context; - } - - SupervisorContext SupervisorContext::FromVariants(const std::vector& variants) { - SupervisorContext context; - context.ParseVariants(variants); - - return context; - } - } -} \ No newline at end of file +namespace service { + +using hurricane::base::Variant; +using hurricane::base::Variants; +using hurricane::base::Serializable; + +SupervisorContext::SupervisorContext() : _spoutCount(0), _boltCount(0) { +} + +void SupervisorContext::Serialize(base::Variants& variants) const +{ + Variant::Serialize(variants, _id); + Variant::Serialize(variants, _spoutCount); + Variant::Serialize(variants, _boltCount); + Variant::Serialize(variants, _freeSpouts); + Variant::Serialize(variants, _freeBolts); + Variant::Serialize(variants, _busySpouts); + Variant::Serialize(variants, _busyBolts); + Variant::Serialize(variants, _taskInfos); + +} + +void SupervisorContext::Deserialize(Variants::const_iterator& it) +{ + Variant::Deserialize(it, _id); + Variant::Deserialize(it, _spoutCount); + Variant::Deserialize(it, _boltCount); + Variant::Deserialize(it, _freeSpouts); + Variant::Deserialize(it, _freeBolts); + Variant::Deserialize(it, _busySpouts); + Variant::Deserialize(it, _busyBolts); + Variant::Deserialize(it, _taskInfos); +} + +} +} diff --git a/src/hurricane/spout/SpoutDeclarer.cpp b/src/hurricane/spout/SpoutDeclarer.cpp index 45c0ba2..d5bf293 100755 --- a/src/hurricane/spout/SpoutDeclarer.cpp +++ b/src/hurricane/spout/SpoutDeclarer.cpp @@ -7,6 +7,12 @@ namespace hurricane { _spout(spout){ SetType(hurricane::task::TaskDeclarer::Type::Spout); SetTaskName(spoutName); + + _fields = _spout->DeclareFields(); + int fieldIndex = 0; + for ( const std::string& field : _fields ) { + _fieldsMap.insert({field, fieldIndex}); + } } } -} \ No newline at end of file +} diff --git a/src/hurricane/task/BoltExecutor.cpp b/src/hurricane/task/BoltExecutor.cpp new file mode 100644 index 0000000..59665c6 --- /dev/null +++ b/src/hurricane/task/BoltExecutor.cpp @@ -0,0 +1,36 @@ +#include "hurricane/task/BoltExecutor.h" +#include "hurricane/bolt/IBolt.h" +#include "hurricane/collector/TaskQueue.h" + +namespace hurricane { +namespace task { + +BoltExecutor::BoltExecutor() +{ + _loop.MessageMap(Executor::MessageType::OnTuple, this, &BoltExecutor::OnTuple); +} + +void BoltExecutor::Start() +{ + _thread = std::thread(&BoltExecutor::StartLoop, this); +} + +void BoltExecutor::OnTuple(message::Message& message) +{ +} + +void BoltExecutor::StartLoop() +{ + collector::TaskItem* taskItem; + + while ( _taskQueue->Pop(taskItem) ) { + _bolt->Execute(taskItem->GetTuple()); + + delete taskItem; + taskItem = nullptr; + } +} + + +} +} diff --git a/src/hurricane/task/Executor.cpp b/src/hurricane/task/Executor.cpp new file mode 100644 index 0000000..95e488c --- /dev/null +++ b/src/hurricane/task/Executor.cpp @@ -0,0 +1 @@ +#include "hurricane/task/Executor.h" diff --git a/src/hurricane/task/PathInfo.cpp b/src/hurricane/task/PathInfo.cpp new file mode 100644 index 0000000..4f6f62b --- /dev/null +++ b/src/hurricane/task/PathInfo.cpp @@ -0,0 +1,40 @@ +#include "hurricane/task/TaskInfo.h" +#include "hurricane/base/Variant.h" + +namespace hurricane { +namespace task { + +using hurricane::base::Variant; +using hurricane::base::Variants; +using hurricane::base::Serializable; + +void PathInfo::Serialize(base::Variants& variants) const +{ + Variant::Serialize(variants, _groupMethod); + Variant::Serialize(variants, _destinationTask); + Variant::Serialize(variants, _fieldName); + Variant::Serialize(variants, _destinationExecutors); +} + +void PathInfo::Deserialize(Variants::const_iterator& it) +{ + Variant::Deserialize(it, _groupMethod); + Variant::Deserialize(it, _destinationTask); + Variant::Deserialize(it, _fieldName); + Variant::Deserialize(it, _destinationExecutors); +} + +void ExecutorPosition::Serialize(base::Variants& variants) const +{ + Variant::Serialize(variants, _supervisor); + Variant::Serialize(variants, _executorIndex); +} + +void ExecutorPosition::Deserialize(Variants::const_iterator& it) +{ + Variant::Deserialize(it, _supervisor); + Variant::Deserialize(it, _executorIndex); +} + +} +} diff --git a/src/hurricane/task/SpoutExecutor.cpp b/src/hurricane/task/SpoutExecutor.cpp new file mode 100644 index 0000000..b5d71d5 --- /dev/null +++ b/src/hurricane/task/SpoutExecutor.cpp @@ -0,0 +1,43 @@ +#include "hurricane/task/SpoutExecutor.h" +#include "hurricane/spout/ISpout.h" +#include + +namespace hurricane { +namespace task { + +SpoutExecutor::SpoutExecutor() +{ + +} + +void SpoutExecutor::Start() +{ + _thread = std::thread(&SpoutExecutor::MainLoop, this); +} + +void SpoutExecutor::SetSpout(spout::ISpout* spout) +{ + _spout.reset(spout); +} + +void SpoutExecutor::MainLoop() +{ + int flowTime = 1000 * 1000 / _flowParam; + while ( true ) { + _spout->NextTuple(); + std::this_thread::sleep_for(std::chrono::microseconds(flowTime)); + } +} + +int SpoutExecutor::GetFlowParam() const +{ + return _flowParam; +} + +void SpoutExecutor::SetFlowParam(int flowParam) +{ + _flowParam = flowParam; +} + +} +} diff --git a/src/hurricane/task/TaskInfo.cpp b/src/hurricane/task/TaskInfo.cpp new file mode 100644 index 0000000..599581a --- /dev/null +++ b/src/hurricane/task/TaskInfo.cpp @@ -0,0 +1,27 @@ +#include "hurricane/task/TaskInfo.h" + +namespace hurricane { +namespace task { + +using hurricane::base::Variant; +using hurricane::base::Variants; +using hurricane::base::Serializable; + +void TaskInfo::Serialize(base::Variants& variants) const +{ + Variant::Serialize(variants, _topologyName); + Variant::Serialize(variants, _taskName); + Variant::Serialize(variants, _paths); + Variant::Serialize(variants, _executorIndex); +} + +void TaskInfo::Deserialize(base::Variants::const_iterator& it) +{ + Variant::Deserialize(it, _topologyName); + Variant::Deserialize(it, _taskName); + Variant::Deserialize(it, _paths); + Variant::Deserialize(it, _executorIndex); +} + +} +} diff --git a/src/hurricane/tool/StartNimbus.cpp b/src/hurricane/tool/StartNimbus.cpp index 29d6402..41e67c7 100755 --- a/src/hurricane/tool/StartNimbus.cpp +++ b/src/hurricane/tool/StartNimbus.cpp @@ -20,9 +20,9 @@ int main(int argc, char *argv[]) } void StartNimbus(const std::string& configFileName) { - hurricane::util::Configuration supervisorConfiguration; - supervisorConfiguration.Parse(configFileName); + hurricane::util::Configuration nimbusConfigratuion; + nimbusConfigratuion.Parse(configFileName); - hurricane::service::Nimbus nimbus(hurricane::base::NetAddress("127.0.0.1", 6009)); + hurricane::service::Nimbus nimbus(nimbusConfigratuion); nimbus.StartListen(); -} \ No newline at end of file +} diff --git a/src/hurricane/tool/StartSupervisor.cpp b/src/hurricane/tool/StartSupervisor.cpp index e85f368..e4bc0d5 100755 --- a/src/hurricane/tool/StartSupervisor.cpp +++ b/src/hurricane/tool/StartSupervisor.cpp @@ -28,7 +28,7 @@ void StartSupervisor(const std::string& configFileName) { std::cout << supervisorConfiguration.GetProperty("supervisor.host") << std::endl; std::cout << supervisorConfiguration.GetIntegerProperty("supervisor.port") << std::endl; - hurricane::service::Supervisor supervisor(supervisorConfiguration); + hurricane::service::Supervisor supervisor(supervisorConfiguration); supervisor.JoinNimbus([&supervisor](const hurricane::message::Response& response) { if ( response.GetStatus() != hurricane::message::Response::Status::Successful ) { std::cerr << "Can't join nimbus." << std::endl; @@ -36,7 +36,10 @@ void StartSupervisor(const std::string& configFileName) { exit(EXIT_FAILURE); } + else { + std::cout << "Join successfully" << std::endl; + } - supervisor.StartListen(); + supervisor.StartListen(); }); } diff --git a/src/hurricane/topology/TopologyLoader.cpp b/src/hurricane/topology/TopologyLoader.cpp index 22202b9..aa9525e 100755 --- a/src/hurricane/topology/TopologyLoader.cpp +++ b/src/hurricane/topology/TopologyLoader.cpp @@ -1,4 +1,5 @@ #include "hurricane/topology/TopologyLoader.h" +#include "hurricane/base/Library.h" #include "sample/wordcount/WordCountTopology.h" namespace hurricane { @@ -9,14 +10,21 @@ namespace hurricane { return instance; } - TopologyLoader::TopologyLoader() { - Topology* topology = ::GetTopology(); - - _topologies.insert({ topology->GetName(), std::shared_ptr(topology) }); + TopologyLoader::TopologyLoader() { } std::shared_ptr TopologyLoader::GetTopology(const std::string& topologyName) { + if ( _libraryHandles.find(topologyName) == _libraryHandles.end() ) { + LibraryHandle libraryHandle = HurricaneLibraryLoad(topologyName); + _libraryHandles[topologyName] = libraryHandle; + TopologyGetter topologyGetter = + HurricaneLibraryGetSymbol(libraryHandle, "GetTopology"); + + std::cout << "Getter: " << topologyGetter << std::endl; + _topologies[topologyName].reset(topologyGetter()); + } + return _topologies[topologyName]; } } -} \ No newline at end of file +} diff --git a/src/hurricane/util/NetConnector.cpp b/src/hurricane/util/NetConnector.cpp index add099e..a35ee71 100755 --- a/src/hurricane/util/NetConnector.cpp +++ b/src/hurricane/util/NetConnector.cpp @@ -17,6 +17,7 @@ */ #include "hurricane/util/NetConnector.h" +#include namespace hurricane { namespace util { @@ -24,9 +25,18 @@ namespace hurricane { void NetConnector::Connect() { - _client = std::make_shared(); - - _client->Connect(_host.GetHost(), _host.GetPort()); + if ( !_client.get() ) { + _client = std::make_shared(); + + try { + _client->Connect(_host.GetHost(), _host.GetPort()); + } + catch ( const std::exception& e ) { + std::cout << "Release client" << std::endl; + _client.reset(); + throw e; + } + } } void NetConnector::Connect(ConnectCallback callback) { @@ -41,8 +51,8 @@ namespace hurricane { } void NetConnector::SendAndReceive(const char* buffer, int32_t size, DataReceiver receiver) - { - _client->Send(buffer, size); + { + _client->Send(buffer, size); char resultBuffer[RECEIVE_BUFFER_SIZE]; int readSize = _client->Receive(resultBuffer, RECEIVE_BUFFER_SIZE); diff --git a/src/hurricane/util/NetListener.cpp b/src/hurricane/util/NetListener.cpp index 3c8b4aa..4be62d4 100755 --- a/src/hurricane/util/NetListener.cpp +++ b/src/hurricane/util/NetListener.cpp @@ -20,8 +20,8 @@ #include #include #include - -namespace hurricane { + +namespace hurricane { namespace util { const int DATA_BUFFER_SIZE = 65535; @@ -29,8 +29,8 @@ namespace hurricane { { _server = std::make_shared(); - _server->Listen(_host.GetHost(), _host.GetPort()); - std::cout << "Listen on " << _host.GetHost() << ":" << _host.GetPort() << std::endl; + _server->Listen(_host.GetHost(), _host.GetPort()); + std::cout << "Listen on " << _host.GetHost() << ":" << _host.GetPort() << std::endl; while ( 1 ) { diff --git a/src/hurricane/util/StringUtil.cpp b/src/hurricane/util/StringUtil.cpp index 7e399bc..c9e5ef2 100755 --- a/src/hurricane/util/StringUtil.cpp +++ b/src/hurricane/util/StringUtil.cpp @@ -20,6 +20,7 @@ #include #include #include +#include using std::vector; using std::string; @@ -73,3 +74,23 @@ std::string RandomString(const std::string & candidate, int length) return result; } + +std::string Int2String(int value) +{ + std::ostringstream os; + os << value; + + return os.str(); +} + +std::string JoinStrings(const std::vector& words) +{ + std::string sentence; + for ( const std::string& word : words ) { + sentence += word + ' '; + } + + sentence.pop_back(); + + return sentence; +} diff --git a/src/sample/wordcount/HelloWorldSpout.cpp b/src/sample/wordcount/HelloWorldSpout.cpp index 9e9a682..9ef3f8f 100755 --- a/src/sample/wordcount/HelloWorldSpout.cpp +++ b/src/sample/wordcount/HelloWorldSpout.cpp @@ -1,7 +1,12 @@ #include "sample/wordcount/HelloWorldSpout.h" +#include "hurricane/util/StringUtil.h" +#include +#include +#include void HelloWorldSpout::Prepare(std::shared_ptr outputCollector) { - _outputCollector = outputCollector; + _outputCollector = outputCollector; + _words = SplitString("Hello world there are some words we generate new sentence randomly", ' '); } void HelloWorldSpout::Cleanup() { @@ -12,7 +17,23 @@ std::vector HelloWorldSpout::DeclareFields() { } void HelloWorldSpout::NextTuple() { + static int32_t id = 0; + timeval currentTime; + gettimeofday(¤tTime, nullptr); + + int64_t currentMicroseconds = currentTime.tv_sec; + currentMicroseconds *= 1000000; + currentMicroseconds += currentTime.tv_usec; + + ++ id; + + std::vector words(5); + for ( int i = 0; i < 5; i ++ ) { + words[i] = _words[rand() % _words.size()]; + } + + std::string sentence = JoinStrings(words); _outputCollector->Emit({ - "Hello World" - }); -} \ No newline at end of file + sentence, currentMicroseconds, id + }); +} diff --git a/src/sample/wordcount/SplitSentenceBolt.cpp b/src/sample/wordcount/SplitSentenceBolt.cpp index aa40255..072bde4 100755 --- a/src/sample/wordcount/SplitSentenceBolt.cpp +++ b/src/sample/wordcount/SplitSentenceBolt.cpp @@ -2,7 +2,7 @@ #include "hurricane/util/StringUtil.h" void SplitSentenceBolt::Prepare(std::shared_ptr outputCollector) { - _outputCollector = outputCollector; + _outputCollector = outputCollector; } void SplitSentenceBolt::Cleanup() { @@ -13,10 +13,13 @@ std::vector SplitSentenceBolt::DeclareFields() { } void SplitSentenceBolt::Execute(const hurricane::base::Tuple& tuple) { - std::string sentence = tuple[0].ToString(); - std::vector words = SplitString(sentence, ' '); + std::string sentence = tuple[0].GetStringValue(); + int64_t sourceMicroseconds = tuple[1].GetInt64Value(); + int32_t id = tuple[2].GetInt32Value(); - for ( const std::string& word : words ) { - _outputCollector->Emit({ word }); - } -} \ No newline at end of file + std::vector words = SplitString(sentence, ' '); + + for ( const std::string& word : words ) { + _outputCollector->Emit({ word, sourceMicroseconds, id }); + } +} diff --git a/src/sample/wordcount/WordCountBolt.cpp b/src/sample/wordcount/WordCountBolt.cpp index b3669d6..a5a1c1b 100755 --- a/src/sample/wordcount/WordCountBolt.cpp +++ b/src/sample/wordcount/WordCountBolt.cpp @@ -1,11 +1,22 @@ #include "sample/wordcount/WordCountBolt.h" #include "hurricane/util/StringUtil.h" +#include +#include + +std::string itos(int number) { + std::ostringstream ss; + ss << number; + + return ss.str(); +} void WordCountBolt::Prepare(std::shared_ptr outputCollector) { _outputCollector = outputCollector; + _logFile = new std::ofstream("timestamp" + itos(rand()) + ".txt"); } void WordCountBolt::Cleanup() { + delete _logFile; } std::vector WordCountBolt::DeclareFields() { @@ -13,7 +24,9 @@ std::vector WordCountBolt::DeclareFields() { } void WordCountBolt::Execute(const hurricane::base::Tuple& tuple) { - std::string word = tuple[0].ToString(); + std::string word = tuple[0].GetStringValue(); + int64_t sourceMicroseconds = tuple[1].GetInt64Value(); + int32_t id = tuple[2].GetInt32Value(); auto wordCountIterator = _wordCounts.find(word); if ( wordCountIterator == _wordCounts.end() ) { @@ -23,5 +36,15 @@ void WordCountBolt::Execute(const hurricane::base::Tuple& tuple) { wordCountIterator->second ++; + std::cout << word << ' ' << wordCountIterator->second << std::endl; _outputCollector->Emit({ word, wordCountIterator->second }); -} \ No newline at end of file + + timeval currentTime; + gettimeofday(¤tTime, nullptr); + + int64_t currentMicroseconds = currentTime.tv_sec; + currentMicroseconds *= 1000000; + currentMicroseconds += currentTime.tv_usec; + + *_logFile << sourceMicroseconds << ' ' << currentMicroseconds << std::endl; +} diff --git a/src/sample/wordcount/WordCountTopology.cpp b/src/sample/wordcount/WordCountTopology.cpp index d1814c5..9530b92 100755 --- a/src/sample/wordcount/WordCountTopology.cpp +++ b/src/sample/wordcount/WordCountTopology.cpp @@ -12,12 +12,12 @@ hurricane::topology::Topology* GetTopology() { .ParallismHint(1); topology->SetBolt("split-sentence-bolt", new SplitSentenceBolt) - .Global("hello-world-spout") + .Random("hello-world-spout") .ParallismHint(3); - topology->SetBolt("word-count-bolt", new WordCountBolt) - .Group("split-sentence-bolt", "word") - .ParallismHint(2); + topology->SetBolt("word-count-bolt", new WordCountBolt) + .Field("split-sentence-bolt", "word") + .ParallismHint(2); return topology; -} \ No newline at end of file +} diff --git a/target/run/avg.js b/target/run/avg.js new file mode 100644 index 0000000..6f7c78d --- /dev/null +++ b/target/run/avg.js @@ -0,0 +1,30 @@ +'use strict'; + +const fs = require('fs'); + +const timestampTexts = fs.readFileSync('timestamp.txt', 'utf8'); +const timestamps = timestampTexts.split('\n').map(line => { + line = line.trim(); + const parts = line.split(' '); + + return { + start: Number(parts[0]), + end: Number(parts[1]) + }; +}); + +let totalResponse = 0; +let timestampCount = 0; +timestamps.forEach(timestamp => { + if ( timestamp.start && timestamp.end ) { + totalResponse += (timestamp.end - timestamp.start); + timestampCount ++; + } +}); + +console.log(totalResponse); + +const avg = totalResponse / timestampCount; +console.log(avg); + +// 133 diff --git a/target/run/deploy.sh b/target/run/deploy.sh new file mode 100755 index 0000000..d39f3c9 --- /dev/null +++ b/target/run/deploy.sh @@ -0,0 +1,3 @@ +cp -u ../bin/linux/x64/Release/nimbus . +cp -u ../bin/linux/x64/Release/supervisor . +cp -u ../lib/linux/x64/Release/libwordcount.so . diff --git a/target/run/nimbus b/target/run/nimbus new file mode 100755 index 0000000000000000000000000000000000000000..9c5ad91707a301ba614cc2e8d2fa65950a37dac4 GIT binary patch literal 435502 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 z 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 aX?HQ^9a>-v7sqlhF-?n@s>*`QiTHm=SsfGr literal 0 HcmV?d00001 diff --git a/target/run/supervisor1.properties b/target/run/supervisor1.properties index 5f59d53..515980b 100644 --- a/target/run/supervisor1.properties +++ b/target/run/supervisor1.properties @@ -1,9 +1,13 @@ -nimbus.host = 127.0.0.1 -nimbus.port = 6009 - -supervisor.host = 127.0.0.1 -supervisor.port = 7001 - -supervisor.name = s1 -supervisor.spout.num = 2 -supervisor.bolt.num = 3 +nimbus.host = 127.0.0.1 +nimbus.port = 6009 + +supervisor.host = 127.0.0.1 +supervisor.port = 7001 + +supervisor.name = s1 +supervisor.spout.num = 2 +supervisor.bolt.num = 3 + +topology.name = wordcount + +spout.flow.param = 5000 diff --git a/target/run/supervisor2.properties b/target/run/supervisor2.properties index 58ea45d..21e2b7b 100644 --- a/target/run/supervisor2.properties +++ b/target/run/supervisor2.properties @@ -1,9 +1,13 @@ -nimbus.host = 127.0.0.1 -nimbus.port = 6009 - -supervisor.host = 127.0.0.1 -supervisor.port = 7002 - -supervisor.name = s2 -supervisor.spout.num = 2 -supervisor.bolt.num = 3 +nimbus.host = 127.0.0.1 +nimbus.port = 6009 + +supervisor.host = 127.0.0.1 +supervisor.port = 7002 + +supervisor.name = s2 +supervisor.spout.num = 2 +supervisor.bolt.num = 3 + +topology.name = wordcount + +spout.flow.param = 5000 diff --git a/target/run/supervisor3.properties b/target/run/supervisor3.properties index 50d4131..9809f2d 100644 --- a/target/run/supervisor3.properties +++ b/target/run/supervisor3.properties @@ -1,9 +1,13 @@ -nimbus.host = 127.0.0.1 -nimbus.port = 6009 - -supervisor.host = 127.0.0.1 -supervisor.port = 7003 - -supervisor.name = s3 -supervisor.spout.num = 2 -supervisor.bolt.num = 3 +nimbus.host = 127.0.0.1 +nimbus.port = 6009 + +supervisor.host = 127.0.0.1 +supervisor.port = 7003 + +supervisor.name = s3 +supervisor.spout.num = 2 +supervisor.bolt.num = 3 + +topology.name = wordcount + +spout.flow.param = 5000 diff --git a/tools/build.sh b/tools/build.sh new file mode 100755 index 0000000..9c95843 --- /dev/null +++ b/tools/build.sh @@ -0,0 +1,47 @@ +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +KAKEFILE_DIR="${DIR}/../kake/Hurricane" +BUILD_DIR="${DIR}/../target/build/linux/x64/Release" + +source "${DIR}/deps.sh" + +echo "[INFO] Check NodeJS" +NODE_PATH=$("${DIR}/check_tool.sh" NodeJS node ../deps/node) +NODE_DIR=$(dirname ${NODE_PATH}) + +echo "[INFO] Check Kake" +KAKE_PATH=$("${DIR}/check_tool.sh" Kake kake ../deps/kake) +KAKE_DIR=$(dirname ${KAKE_PATH}) + +echo "${NODE_DIR}" +echo "${KAKE_DIR}" + +if [ -z "${KAKE_DIR}" ]; then + echo "You have not installed kake, do you want to install it?(Y/N)" + read to_install_kake + + if [ "${to_install_kake}" == "N" ]; then + make install + exit 0 + fi +fi + +if [ -z "${NODE_DIR}" ]; then + echo "[INFO] Download NodeJS" + "${DIR}/download_tool.sh" node +fi + +if [ -z "${KAKE_DIR}" ]; then + echo "[INFO] Download Kake" + "${DIR}/download_tool.sh" kake "${KAKE_URL}" +fi + +pushd . + +cd "${KAKEFILE_DIR}" +npm install +"${KAKE_DIR}/kake" generate + +cd "${BUILD_DIR}" +make + +popd diff --git a/tools/check_tool.sh b/tools/check_tool.sh new file mode 100755 index 0000000..b7cb387 --- /dev/null +++ b/tools/check_tool.sh @@ -0,0 +1,15 @@ +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +TOOL_NAME=$1 +TOOL_EXEC=$2 +TOOL_DIR="${DIR}/../deps" + +TOOL_PATH=$(which ${TOOL_EXEC}) + +if [ -z "${TOOL_PATH}" ]; then + if [ -f "${TOOL_DIR}/${TOOL_NAME}" ]; then + echo "${TOOL_DIR}/${TOOL_NAME}" + fi +fi + +echo ${TOOL_PATH} diff --git a/tools/config.sh b/tools/config.sh new file mode 100644 index 0000000..7614a03 --- /dev/null +++ b/tools/config.sh @@ -0,0 +1,2 @@ +export MANAGER_USER=manager +export HURRICANE_HOME=/opt/hurricane diff --git a/tools/deps.sh b/tools/deps.sh new file mode 100644 index 0000000..310ca18 --- /dev/null +++ b/tools/deps.sh @@ -0,0 +1,4 @@ +NODE_EXEC="node" +NODE_URL="" +KAKE_EXEC="kake" +KAKE_URL="http://127.0.0.1:8080/kake.tar.gz" diff --git a/tools/download_tool.sh b/tools/download_tool.sh new file mode 100755 index 0000000..1620259 --- /dev/null +++ b/tools/download_tool.sh @@ -0,0 +1,10 @@ +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +TOOL_NAME=$1 +TOOL_URL=$2 + +wget "${TOOL_URL}" -o "${TOOL_NAME}" + +if [ -f "${DIR}/../deps/${TOOL_NAME}" ]; then + echo "${DIR}/../deps/${TOOL_NAME}" +fi diff --git a/tools/install_managers.sh b/tools/install_managers.sh new file mode 100755 index 0000000..ffeb5ea --- /dev/null +++ b/tools/install_managers.sh @@ -0,0 +1,8 @@ +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +source "${DIR}/config.sh" + +while read node; do + scp "${DIR}../" -r "${MANAGER_USER}"@"$node":"${HURRICANE_HOME}" + ssh "$node" 'cd ${HURRICANE_HOME};tools/build.sh' +done < nodes.list diff --git a/tools/nodes.list b/tools/nodes.list new file mode 100644 index 0000000..8893669 --- /dev/null +++ b/tools/nodes.list @@ -0,0 +1,3 @@ +192.168.101.100 +192.168.101.101 +192.168.101.102