Custom icons work when example is run in TextEditor, but fail in my add-on code?
in particular this line won't produce the same result when running it as an add-on:

script_path = bpy.context.space_data.text.filepath


because the code isn't located in a text data block inside the .blend any longer, but directly on your file-system/ harddisk



to get the script path as an addon you can do

script_path = os.path.dirname(__file__)


or as a one liner set icons_dir:

icons_dir = os.path.join(os.path.dirname(__file__), "icons")






to complete your script. I reused the template in TextEditor, and some variable names may have changed. (good practice to figure out what's different). There's no need to use global keyword, using a dict for the preview_collections lets you avoid that complication. 

bl_info = {
    "name": "FILL ME IN",
    "author": "fauxbar",
    "version": (0, 1),
    "blender": (2, 7, 6),
    "category": "3D View"
}

import os
import bpy

# global variable to store icons in
preview_collections = {}


class ToolsPanel(bpy.types.Panel):
    bl_label = "MyAddon"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"

    def draw(self, context):
        this_icon = preview_collections["custom_icons"]["custom_icon"].icon_id
        self.layout.operator("hello.hello", icon_value=this_icon)


class OBJECT_OT_HelloButton(bpy.types.Operator):
    bl_idname = "hello.hello"
    bl_label = "Generate MyMesh"
    # the icon is told to appear by layout.operator(...icon_value=this_icon) (above)

    def execute(self, context):
        print("Hello world, from space")
        return{'FINISHED'}


class Panel(bpy.types.Panel):
    """Creates a Panel in the 3D view Tools panel"""
    bl_label = "Custom Icon Preview Panel"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"

    def draw(self, context):
        self.layout.row().label('something')


def register():

    import bpy.utils.previews
    pcoll = bpy.utils.previews.new()
    icons_dir = os.path.join(os.path.dirname(__file__), "icons")
    pcoll.load("custom_icon", os.path.join(icons_dir, "icon.png"), 'IMAGE')
    preview_collections["custom_icons"] = pcoll

    bpy.utils.register_module(__name__)


def unregister():

    for pcoll in preview_collections.values():
        bpy.utils.previews.remove(pcoll)
    preview_collections.clear()

    bpy.utils.unregister_module(__name__)


if __name__ == "__main__":
    register()